From 192e36aa98edb9e5bbb6ed04daadacca6ecdef67 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 16 Apr 2026 21:06:57 +0200 Subject: [PATCH 1/3] 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 --- Python/fileutils.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Python/fileutils.c b/Python/fileutils.c index 0c1766b8804500..409d70ace7bd35 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1923,7 +1923,7 @@ _Py_read(int fd, void *buf, size_t count) static Py_ssize_t _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) { - Py_ssize_t n; + Py_ssize_t n = 0; int err; int async_err = 0; @@ -1970,7 +1970,11 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) c /= 2; } while (c > 0); #else - n = write(fd, buf, count); + /* only call write() if there is something to write. + * writing 0 bytes to not a regular file is undefined behaviour. */ + if (count > 0) { + n = write(fd, buf, count); + } #endif /* save/restore errno because PyErr_CheckSignals() * and PyErr_SetFromErrno() can modify it */ @@ -1996,7 +2000,11 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) c /= 2; } while (c > 0); #else - n = write(fd, buf, count); + /* only call write() if there is something to write. + * writing 0 bytes to not a regular file is undefined behaviour. */ + if (count > 0) { + n = write(fd, buf, count); + } #endif err = errno; } while (n < 0 && err == EINTR); From 858726147b1d2438487bf5827757229bc3919d97 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 19 Apr 2026 20:25:35 +0000 Subject: [PATCH 2/3] Add NEWS entry --- .../2026-04-19-20-22-00.gh-issue-148773.LBukI9.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-04-19-20-22-00.gh-issue-148773.LBukI9.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-19-20-22-00.gh-issue-148773.LBukI9.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-19-20-22-00.gh-issue-148773.LBukI9.rst new file mode 100644 index 00000000000000..cd39c59d7cef6e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-19-20-22-00.gh-issue-148773.LBukI9.rst @@ -0,0 +1,4 @@ +Fixes a bug in :func:`print` which could result in a hangup signal (``SIGHUP``) +being sent to the user session under certain circumstances when printing an +empty string to unbuffered stdout, e.g. if ``python3 -u`` is used or the +:envvar:`PYTHONUNBUFFERED` environment variable is set. From 8b5be69a3ff2bb59eeb560aef46e4eaa07e4ade3 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 20 Apr 2026 17:59:22 +0000 Subject: [PATCH 3/3] Reword code comment --- Python/fileutils.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Python/fileutils.c b/Python/fileutils.c index 409d70ace7bd35..743a66b295d30a 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1970,8 +1970,8 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) c /= 2; } while (c > 0); #else - /* only call write() if there is something to write. - * writing 0 bytes to not a regular file is undefined behaviour. */ + /* Only call write() if there is something to write as + * writing 0 bytes to a non-regular file is an undefined behaviour. */ if (count > 0) { n = write(fd, buf, count); } @@ -2000,8 +2000,8 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) c /= 2; } while (c > 0); #else - /* only call write() if there is something to write. - * writing 0 bytes to not a regular file is undefined behaviour. */ + /* Only call write() if there is something to write as + * writing 0 bytes to a non-regular file is an undefined behaviour. */ if (count > 0) { n = write(fd, buf, count); }