Issue #13303: Fix a race condition in the bytecode file creation.

parent 59142db6
...@@ -85,10 +85,11 @@ def _write_atomic(path, data): ...@@ -85,10 +85,11 @@ def _write_atomic(path, data):
Be prepared to handle a FileExistsError if concurrent writing of the Be prepared to handle a FileExistsError if concurrent writing of the
temporary file is attempted.""" temporary file is attempted."""
if not sys.platform.startswith('win'): if not sys.platform.startswith('win'):
# On POSIX-like platforms, renaming is atomic # On POSIX-like platforms, renaming is atomic. id() is used to generate
path_tmp = path + '.tmp' # a pseudo-random filename.
path_tmp = '{}.{}'.format(path, id(path))
fd = _os.open(path_tmp, _os.O_EXCL | _os.O_CREAT | _os.O_WRONLY)
try: try:
fd = _os.open(path_tmp, _os.O_EXCL | _os.O_CREAT | _os.O_WRONLY)
with _io.FileIO(fd, 'wb') as file: with _io.FileIO(fd, 'wb') as file:
file.write(data) file.write(data)
_os.rename(path_tmp, path) _os.rename(path_tmp, path)
......
...@@ -1183,42 +1183,6 @@ parse_source_module(PyObject *pathname, FILE *fp) ...@@ -1183,42 +1183,6 @@ parse_source_module(PyObject *pathname, FILE *fp)
return co; return co;
} }
/* Helper to open a bytecode file for writing in exclusive mode */
#ifndef MS_WINDOWS
static FILE *
open_exclusive(char *filename, mode_t mode)
{
#if defined(O_EXCL)&&defined(O_CREAT)&&defined(O_WRONLY)&&defined(O_TRUNC)
/* Use O_EXCL to avoid a race condition when another process tries to
write the same file. When that happens, our open() call fails,
which is just fine (since it's only a cache).
XXX If the file exists and is writable but the directory is not
writable, the file will never be written. Oh well.
*/
int fd;
(void) unlink(filename);
fd = open(filename, O_EXCL|O_CREAT|O_WRONLY|O_TRUNC
#ifdef O_BINARY
|O_BINARY /* necessary for Windows */
#endif
#ifdef __VMS
, mode, "ctxt=bin", "shr=nil"
#else
, mode
#endif
);
if (fd < 0)
return NULL;
return fdopen(fd, "wb");
#else
/* Best we can do -- on Windows this can't happen anyway */
return fopen(filename, "wb");
#endif
}
#endif
/* Write a compiled module to a file, placing the time of last /* Write a compiled module to a file, placing the time of last
modification of its source into the header. modification of its source into the header.
Errors are ignored, if a write error occurs an attempt is made to Errors are ignored, if a write error occurs an attempt is made to
...@@ -1234,15 +1198,13 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname, ...@@ -1234,15 +1198,13 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
#ifdef MS_WINDOWS /* since Windows uses different permissions */ #ifdef MS_WINDOWS /* since Windows uses different permissions */
mode_t mode = srcstat->st_mode & ~S_IEXEC; mode_t mode = srcstat->st_mode & ~S_IEXEC;
#else #else
mode_t mode = srcstat->st_mode & ~S_IXUSR & ~S_IXGRP & ~S_IXOTH;
mode_t dirmode = (srcstat->st_mode | mode_t dirmode = (srcstat->st_mode |
S_IXUSR | S_IXGRP | S_IXOTH | S_IXUSR | S_IXGRP | S_IXOTH |
S_IWUSR | S_IWGRP | S_IWOTH); S_IWUSR | S_IWGRP | S_IWOTH);
PyObject *dirbytes; PyObject *dirbytes;
#endif #endif
#ifdef MS_WINDOWS
int fd; int fd;
#else #ifndef MS_WINDOWS
PyObject *cpathbytes, *cpathbytes_tmp; PyObject *cpathbytes, *cpathbytes_tmp;
Py_ssize_t cpathbytes_len; Py_ssize_t cpathbytes_len;
#endif #endif
...@@ -1313,7 +1275,7 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname, ...@@ -1313,7 +1275,7 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
return; return;
} }
cpathbytes_len = PyBytes_GET_SIZE(cpathbytes); cpathbytes_len = PyBytes_GET_SIZE(cpathbytes);
cpathbytes_tmp = PyBytes_FromStringAndSize(NULL, cpathbytes_len + 4); cpathbytes_tmp = PyBytes_FromStringAndSize(NULL, cpathbytes_len + 6);
if (cpathbytes_tmp == NULL) { if (cpathbytes_tmp == NULL) {
Py_DECREF(cpathbytes); Py_DECREF(cpathbytes);
PyErr_Clear(); PyErr_Clear();
...@@ -1321,9 +1283,13 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname, ...@@ -1321,9 +1283,13 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname,
} }
memcpy(PyBytes_AS_STRING(cpathbytes_tmp), PyBytes_AS_STRING(cpathbytes), memcpy(PyBytes_AS_STRING(cpathbytes_tmp), PyBytes_AS_STRING(cpathbytes),
cpathbytes_len); cpathbytes_len);
memcpy(PyBytes_AS_STRING(cpathbytes_tmp) + cpathbytes_len, ".tmp", 4); memcpy(PyBytes_AS_STRING(cpathbytes_tmp) + cpathbytes_len, "XXXXXX", 6);
fp = open_exclusive(PyBytes_AS_STRING(cpathbytes_tmp), mode); fd = mkstemp(PyBytes_AS_STRING(cpathbytes_tmp));
if (0 <= fd)
fp = fdopen(fd, "wb");
else
fp = NULL;
#endif #endif
if (fp == NULL) { if (fp == NULL) {
if (Py_VerboseFlag) if (Py_VerboseFlag)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment