diff options
| author | Alexey Dobriyan <[email protected]> | 2018-01-18 16:34:05 -0800 | 
|---|---|---|
| committer | Linus Torvalds <[email protected]> | 2018-01-19 10:09:41 -0800 | 
| commit | 8bb2ee192e482c5d500df9f2b1b26a560bd3026f (patch) | |
| tree | 2b987957fde2af64c8b5fda429464c42370e2b2e /fs/proc/array.c | |
| parent | 883d50f56d263f70fd73c0d96b09eb36c34e9305 (diff) | |
proc: fix coredump vs read /proc/*/stat race
do_task_stat() accesses IP and SP of a task without bumping reference
count of a stack (which became an entity with independent lifetime at
some point).
Steps to reproduce:
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/time.h>
    #include <sys/resource.h>
    #include <unistd.h>
    #include <sys/wait.h>
    int main(void)
    {
    	setrlimit(RLIMIT_CORE, &(struct rlimit){});
    	while (1) {
    		char buf[64];
    		char buf2[4096];
    		pid_t pid;
    		int fd;
    		pid = fork();
    		if (pid == 0) {
    			*(volatile int *)0 = 0;
    		}
    		snprintf(buf, sizeof(buf), "/proc/%u/stat", pid);
    		fd = open(buf, O_RDONLY);
    		read(fd, buf2, sizeof(buf2));
    		close(fd);
    		waitpid(pid, NULL, 0);
    	}
    	return 0;
    }
    BUG: unable to handle kernel paging request at 0000000000003fd8
    IP: do_task_stat+0x8b4/0xaf0
    PGD 800000003d73e067 P4D 800000003d73e067 PUD 3d558067 PMD 0
    Oops: 0000 [#1] PREEMPT SMP PTI
    CPU: 0 PID: 1417 Comm: a.out Not tainted 4.15.0-rc8-dirty #2
    Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1.fc27 04/01/2014
    RIP: 0010:do_task_stat+0x8b4/0xaf0
    Call Trace:
     proc_single_show+0x43/0x70
     seq_read+0xe6/0x3b0
     __vfs_read+0x1e/0x120
     vfs_read+0x84/0x110
     SyS_read+0x3d/0xa0
     entry_SYSCALL_64_fastpath+0x13/0x6c
    RIP: 0033:0x7f4d7928cba0
    RSP: 002b:00007ffddb245158 EFLAGS: 00000246
    Code: 03 b7 a0 01 00 00 4c 8b 4c 24 70 4c 8b 44 24 78 4c 89 74 24 18 e9 91 f9 ff ff f6 45 4d 02 0f 84 fd f7 ff ff 48 8b 45 40 48 89 ef <48> 8b 80 d8 3f 00 00 48 89 44 24 20 e8 9b 97 eb ff 48 89 44 24
    RIP: do_task_stat+0x8b4/0xaf0 RSP: ffffc90000607cc8
    CR2: 0000000000003fd8
John Ogness said: for my tests I added an else case to verify that the
race is hit and correctly mitigated.
Link: http://lkml.kernel.org/r/20180116175054.GA11513@avx2
Signed-off-by: Alexey Dobriyan <[email protected]>
Reported-by: "Kohli, Gaurav" <[email protected]>
Tested-by: John Ogness <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Oleg Nesterov <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
Diffstat (limited to 'fs/proc/array.c')
| -rw-r--r-- | fs/proc/array.c | 7 | 
1 files changed, 5 insertions, 2 deletions
| diff --git a/fs/proc/array.c b/fs/proc/array.c index 79375fc115d2..d67a72dcb92c 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -430,8 +430,11 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,  		 * safe because the task has stopped executing permanently.  		 */  		if (permitted && (task->flags & PF_DUMPCORE)) { -			eip = KSTK_EIP(task); -			esp = KSTK_ESP(task); +			if (try_get_task_stack(task)) { +				eip = KSTK_EIP(task); +				esp = KSTK_ESP(task); +				put_task_stack(task); +			}  		}  	} |