CVE-2014-1858
Description
A symlink race in NumPy's f2py __init__.py allows local users to overwrite arbitrary files via a vulnerable mktemp call (up to 1.8.0).
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A symlink race in NumPy's f2py __init__.py allows local users to overwrite arbitrary files via a vulnerable mktemp call (up to 1.8.0).
Vulnerability
In NumPy versions before 1.8.1, the f2py module in numpy/f2py/__init__.py uses tempfile.mktemp() to create a temporary file name. The line fname = os.path.join(tempfile.mktemp()+'.f') generates a predictable temporary file path. mktemp() is inherently unsafe because it does not atomically create the file, leaving a window for a symlink attack. This affects all versions of NumPy up to and including 1.8.0 [1][2][3].
Exploitation
An attacker with local access to the system where f2py is executed can exploit the race condition by pre‑creating a symbolic link at the predicted temporary file path, pointing to a victim file. When f2py opens the temporary file for writing (f = open(fname,'w')), the write operation follows the symlink and overwrites the target file. No authentication is needed beyond the ability to create symlinks in the directory where the temporary file is placed (typically /tmp) [1][4].
Impact
Successful exploitation allows an attacker to write arbitrary content to any file that the user running f2py can write to. This leads to data integrity loss and potential privilege escalation if the overwritten file is critical (e.g., a configuration file or a script executed by a privileged process) [1][4].
Mitigation
The vulnerability is fixed in NumPy version 1.8.1, released on 2014-02-09. The fix replaces the insecure mktemp call with a safe alternative (e.g., tempfile.mkstemp). Users should upgrade to NumPy 1.8.1 or later. No workaround is available for users unable to upgrade; the only reliable mitigation is to apply the update [3].
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
numpyPyPI | < 1.8.1 | 1.8.1 |
Affected products
1Patches
10bb46c1448b0ENH: remove insecure mktemp use
5 files changed · +50 −51
numpy/core/tests/test_memmap.py+16 −18 modified@@ -1,7 +1,7 @@ from __future__ import division, absolute_import, print_function import sys -from tempfile import NamedTemporaryFile, TemporaryFile, mktemp +from tempfile import NamedTemporaryFile, TemporaryFile import os from numpy import memmap @@ -33,12 +33,11 @@ def test_roundtrip(self): assert_array_equal(self.data, newfp) def test_open_with_filename(self): - tmpname = mktemp('', 'mmap') - fp = memmap(tmpname, dtype=self.dtype, mode='w+', - shape=self.shape) - fp[:] = self.data[:] - del fp - os.unlink(tmpname) + with NamedTemporaryFile() as tmp: + fp = memmap(tmp.name, dtype=self.dtype, mode='w+', + shape=self.shape) + fp[:] = self.data[:] + del fp def test_unnamed_file(self): with TemporaryFile() as f: @@ -55,17 +54,16 @@ def test_attributes(self): del fp def test_filename(self): - tmpname = mktemp('', 'mmap') - fp = memmap(tmpname, dtype=self.dtype, mode='w+', - shape=self.shape) - abspath = os.path.abspath(tmpname) - fp[:] = self.data[:] - self.assertEqual(abspath, fp.filename) - b = fp[:1] - self.assertEqual(abspath, b.filename) - del b - del fp - os.unlink(tmpname) + with NamedTemporaryFile() as tmp: + fp = memmap(tmp.name, dtype=self.dtype, mode='w+', + shape=self.shape) + abspath = os.path.abspath(tmp.name) + fp[:] = self.data[:] + self.assertEqual(abspath, fp.filename) + b = fp[:1] + self.assertEqual(abspath, b.filename) + del b + del fp def test_filename_fileobj(self): fp = memmap(self.tmpfp, dtype=self.dtype, mode="w+",
numpy/core/tests/test_multiarray.py+3 −10 modified@@ -2316,12 +2316,11 @@ def setUp(self): self.x = rand(shape) + rand(shape).astype(np.complex)*1j self.x[0,:, 1] = [nan, inf, -inf, nan] self.dtype = self.x.dtype - self.filename = tempfile.mktemp() + self.file = tempfile.NamedTemporaryFile() + self.filename = self.file.name def tearDown(self): - if os.path.isfile(self.filename): - os.unlink(self.filename) - #tmp_file.close() + self.file.close() def test_bool_fromstring(self): v = np.array([True, False, True, False], dtype=np.bool_) @@ -2349,7 +2348,6 @@ def test_roundtrip_file(self): y = np.fromfile(f, dtype=self.dtype) f.close() assert_array_equal(y, self.x.flat) - os.unlink(self.filename) def test_roundtrip_filename(self): self.x.tofile(self.filename) @@ -2402,8 +2400,6 @@ def test_file_position_after_fromfile(self): f.close() assert_equal(pos, 10, err_msg=err_msg) - os.unlink(self.filename) - def test_file_position_after_tofile(self): # gh-4118 sizes = [io.DEFAULT_BUFFER_SIZE//8, @@ -2431,8 +2427,6 @@ def test_file_position_after_tofile(self): f.close() assert_equal(pos, 10, err_msg=err_msg) - os.unlink(self.filename) - def _check_from(self, s, value, **kw): y = np.fromstring(asbytes(s), **kw) assert_array_equal(y, value) @@ -2535,7 +2529,6 @@ def test_tofile_sep(self): s = f.read() f.close() assert_equal(s, '1.51,2.0,3.51,4.0') - os.unlink(self.filename) def test_tofile_format(self): x = np.array([1.51, 2, 3.51, 4], dtype=float)
numpy/f2py/f2py2e.py+2 −2 modified@@ -91,7 +91,7 @@ --lower is assumed with -h key, and --no-lower without -h key. --build-dir <dirname> All f2py generated files are created in <dirname>. - Default is tempfile.mktemp(). + Default is tempfile.mkdtemp(). --overwrite-signature Overwrite existing signature file. @@ -424,7 +424,7 @@ def run_compile(): del sys.argv[i] else: remove_build_dir = 1 - build_dir = os.path.join(tempfile.mktemp()) + build_dir = tempfile.mkdtemp() _reg1 = re.compile(r'[-][-]link[-]') sysinfo_flags = [_m for _m in sys.argv[1:] if _reg1.match(_m)]
numpy/f2py/__init__.py+13 −13 modified@@ -28,20 +28,20 @@ def compile(source, from numpy.distutils.exec_command import exec_command import tempfile if source_fn is None: - fname = os.path.join(tempfile.mktemp()+'.f') + f = tempfile.NamedTemporaryFile(suffix='.f') else: - fname = source_fn - - f = open(fname, 'w') - f.write(source) - f.close() - - args = ' -c -m %s %s %s'%(modulename, fname, extra_args) - c = '%s -c "import numpy.f2py as f2py2e;f2py2e.main()" %s' %(sys.executable, args) - s, o = exec_command(c) - if source_fn is None: - try: os.remove(fname) - except OSError: pass + f = open(source_fn, 'w') + + try: + f.write(source) + f.flush() + + args = ' -c -m %s %s %s'%(modulename, f.name, extra_args) + c = '%s -c "import numpy.f2py as f2py2e;f2py2e.main()" %s' % \ + (sys.executable, args) + s, o = exec_command(c) + finally: + f.close() return s from numpy.testing import Tester
numpy/lib/tests/test_io.py+16 −8 modified@@ -4,7 +4,9 @@ import gzip import os import threading -from tempfile import mkstemp, mktemp, NamedTemporaryFile +import shutil +import contextlib +from tempfile import mkstemp, mkdtemp, NamedTemporaryFile import time import warnings import gc @@ -21,6 +23,12 @@ assert_raises, run_module_suite) from numpy.testing import assert_warns, assert_, build_err_msg +@contextlib.contextmanager +def tempdir(change_dir=False): + tmpdir = mkdtemp() + yield tmpdir + shutil.rmtree(tmpdir) + class TextIO(BytesIO): """Helper IO class. @@ -177,14 +185,14 @@ def roundtrip(self, *args, **kwargs): @np.testing.dec.slow def test_big_arrays(self): L = (1 << 31) + 100000 - tmp = mktemp(suffix='.npz') a = np.empty(L, dtype=np.uint8) - np.savez(tmp, a=a) - del a - npfile = np.load(tmp) - a = npfile['a'] - npfile.close() - os.remove(tmp) + with tempdir() as tmpdir: + tmp = open(os.path.join(tmpdir, "file.npz"), "w") + np.savez(tmp, a=a) + del a + npfile = np.load(tmp) + a = npfile['a'] + npfile.close() def test_multiple_arrays(self): a = np.array([[1, 2], [3, 4]], float)
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
13- lists.fedoraproject.org/pipermail/package-announce/2014-February/128358.htmlghsavendor-advisoryx_refsource_FEDORAWEB
- lists.fedoraproject.org/pipermail/package-announce/2014-February/128781.htmlghsavendor-advisoryx_refsource_FEDORAWEB
- github.com/advisories/GHSA-cw6w-4rcx-xphcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2014-1858ghsaADVISORY
- www.openwall.com/lists/oss-security/2014/02/08/3ghsamailing-listx_refsource_MLISTWEB
- www.securityfocus.com/bid/65441ghsavdb-entryx_refsource_BIDWEB
- bugs.debian.org/cgi-bin/bugreport.cgighsax_refsource_CONFIRMWEB
- bugzilla.redhat.com/show_bug.cgighsax_refsource_CONFIRMWEB
- exchange.xforce.ibmcloud.com/vulnerabilities/91318ghsavdb-entryx_refsource_XFWEB
- github.com/numpy/numpy/blob/maintenance/1.8.x/doc/release/1.8.1-notes.rstghsax_refsource_CONFIRMWEB
- github.com/numpy/numpy/commit/0bb46c1448b0d3f5453d5182a17ea7ac5854ee15ghsax_refsource_CONFIRMWEB
- github.com/numpy/numpy/pull/4262ghsax_refsource_CONFIRMWEB
- github.com/pypa/advisory-database/tree/main/vulns/numpy/PYSEC-2018-33.yamlghsaWEB
News mentions
0No linked articles in our index yet.