prevent bypass of guarantee that suids start with fd 0/1/2 open
authorRich Felker <dalias@aerifal.cx>
Thu, 5 Apr 2018 15:04:21 +0000 (11:04 -0400)
committerRich Felker <dalias@aerifal.cx>
Thu, 5 Apr 2018 15:04:21 +0000 (11:04 -0400)
it was reported by Erik Bosman that poll fails without setting revents
when the nfds argument exceeds the current value for RLIMIT_NOFILE,
causing the subsequent open calls to be bypassed. if the rlimit is
either 1 or 2, this leaves fd 0 and 1 potentially closed but openable
when the application code is reached.

based on a brief reading of the poll syscall documentation and code,
it may be possible for poll to fail under other attacker-controlled
conditions as well. if it turns out these are reasonable conditions
that may happen in the real world, we may have to go back and
implement fallbacks to probe each fd individually if poll fails, but
for now, keep things simple and treat all poll failures as fatal.

src/env/__libc_start_main.c

index 2d758af760b215aa42f61004938abb35132916f9..0583f686ed9b10ea6d373ce8c6c2a60a5b3b929d 100644 (file)
@@ -42,11 +42,13 @@ void __init_libc(char **envp, char *pn)
                && !aux[AT_SECURE]) return;
 
        struct pollfd pfd[3] = { {.fd=0}, {.fd=1}, {.fd=2} };
+       int r =
 #ifdef SYS_poll
        __syscall(SYS_poll, pfd, 3, 0);
 #else
        __syscall(SYS_ppoll, pfd, 3, &(struct timespec){0}, 0, _NSIG/8);
 #endif
+       if (r<0) a_crash();
        for (i=0; i<3; i++) if (pfd[i].revents&POLLNVAL)
                if (__sys_open("/dev/null", O_RDWR)<0)
                        a_crash();