![]() |
|
Message-ID: <20250423110608.1050929-1-caleb@postmarketos.org> Date: Wed, 23 Apr 2025 13:05:08 +0200 From: Casey Connolly <casey.connolly@...aro.org> To: musl@...ts.openwall.com Cc: Casey Connolly <casey@...tmarketos.org> Subject: [PATCH] stdio: don't write empty iovec first From: Casey Connolly <casey@...tmarketos.org> When buffering on a FILE is disabled the first iovec will be empty, this results in an empty write which usually has no effect, but when the write is to a virtual filesystem like sysfs or procfs it will still call into the kernel handlers. In some cases like /proc/$PID/uid_map where a specific format is expected the handler will return an error and the entire write will be aborted. Avoid this scenario by skipping the first iovec if it is empty. Signed-off-by: Casey Connolly <casey@...tmarketos.org> --- A simple reproducer for this using uid_map is as follows: #define _GNU_SOURCE #include <stdio.h> #include <unistd.h> #include <sched.h> int main(int argc, char **argv) { int ret, pid; char path[64] = { 0 }; pid = fork(); /* Parent unshares the user namespace so that the child can set its uid_map */ if (pid) { unshare(CLONE_NEWUSER); sleep(2); } else sleep(1); /* Write the uid_map as the child... */ snprintf(path, 64, "/proc/%u/uid_map", getpid()); FILE *f = fopen(path, "w"); /* This setvbuf() makes the write fail with -EINVAL!!! */ setvbuf(f, NULL, _IONBF, 0); /* Now this called into __stdio_write() which creates two iovecs, the first is empty... */ // writev(3, [{iov_base="", iov_len=0}, {iov_base="0 10000 1\n", iov_len=10}], 2) = -1 EINVAL (Invalid argument) ret = fputs("0 0 1\n", f); return ret; } --- src/stdio/__stdio_write.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/stdio/__stdio_write.c b/src/stdio/__stdio_write.c index d2d89475b0f9..89cb9917cca4 100644 --- a/src/stdio/__stdio_write.c +++ b/src/stdio/__stdio_write.c @@ -9,8 +9,14 @@ size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len) }; struct iovec *iov = iovs; size_t rem = iov[0].iov_len + iov[1].iov_len; int iovcnt = 2; + + /* Don't write an empty iovec, this can confuse some kernel APIs */ + if (!iov->iov_len) { + iov++; + iovcnt = 1; + } ssize_t cnt; for (;;) { cnt = syscall(SYS_writev, f->fd, iov, iovcnt); if (cnt == rem) { -- 2.49.0
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.