Skip to content

Commit 192e36a

Browse files
committed
Fix undefined behaviour in write() call
write()ing zero bytes to not a regular file (e.g. stdout) is undefined behaviour and can generate a SIGHUP under certain circumstances, e.g. on AIX when running with unbuffered stdout within sudo, screen, tmux: $ sudo python3 -u -c 'print(""); print("foo")' Hangup Python's print() implementation in this case calls write() twice: # truss python3 -u -c 'print("")' [...] kwrite(1, 0x08001000A001B910, 0) = 0 kwrite(1, "\n", 1) = 1 However, the first write() is undefined behaviour. ssize_t write(int fildes, const void *buf, size_t nbyte); The write() function shall attempt to write nbyte bytes from the buffer pointed to by buf to the file associated with the open file descriptor, fildes. Before any action described below is taken, and if nbyte is zero and the file is a regular file, the write() function may detect and return errors as described below. In the absence of errors, or if error detection is not performed, the write() function shall return zero and have no other results. If nbyte is zero and the file is not a regular file, the results are unspecified. https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html
1 parent 2faceee commit 192e36a

File tree

1 file changed

+11
-3
lines changed

1 file changed

+11
-3
lines changed

Python/fileutils.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,7 +1923,7 @@ _Py_read(int fd, void *buf, size_t count)
19231923
static Py_ssize_t
19241924
_Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
19251925
{
1926-
Py_ssize_t n;
1926+
Py_ssize_t n = 0;
19271927
int err;
19281928
int async_err = 0;
19291929

@@ -1970,7 +1970,11 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
19701970
c /= 2;
19711971
} while (c > 0);
19721972
#else
1973-
n = write(fd, buf, count);
1973+
/* only call write() if there is something to write.
1974+
* writing 0 bytes to not a regular file is undefined behaviour. */
1975+
if (count > 0) {
1976+
n = write(fd, buf, count);
1977+
}
19741978
#endif
19751979
/* save/restore errno because PyErr_CheckSignals()
19761980
* and PyErr_SetFromErrno() can modify it */
@@ -1996,7 +2000,11 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held)
19962000
c /= 2;
19972001
} while (c > 0);
19982002
#else
1999-
n = write(fd, buf, count);
2003+
/* only call write() if there is something to write.
2004+
* writing 0 bytes to not a regular file is undefined behaviour. */
2005+
if (count > 0) {
2006+
n = write(fd, buf, count);
2007+
}
20002008
#endif
20012009
err = errno;
20022010
} while (n < 0 && err == EINTR);

0 commit comments

Comments
 (0)