From b9f99303b1d5a9f07614d0d8f0aaa6a623c3d206 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 27 Feb 2017 14:28:24 -0800 Subject: [PATCH] ipc/shm: Fix shmat mmap nil-page protection The issue is described here, with a nice testcase: https://bugzilla.kernel.org/show_bug.cgi?id=192931 The problem is that shmat() calls do_mmap_pgoff() with MAP_FIXED, and the address rounded down to 0. For the regular mmap case, the protection mentioned above is that the kernel gets to generate the address -- arch_get_unmapped_area() will always check for MAP_FIXED and return that address. So by the time we do security_mmap_addr(0) things get funky for shmat(). The testcase itself shows that while a regular user crashes, root will not have a problem attaching a nil-page. There are two possible fixes to this. The first, and which this patch does, is to simply allow root to crash as well -- this is also regular mmap behavior, ie when hacking up the testcase and adding mmap(... |MAP_FIXED). While this approach is the safer option, the second alternative is to ignore SHM_RND if the rounded address is 0, thus only having MAP_SHARED flags. This makes the behavior of shmat() identical to the mmap() case. The downside of this is obviously user visible, but does make sense in that it maintains semantics after the round-down wrt 0 address and mmap. Passes shm related ltp tests. Change-Id: I3e7008590648e790d066a42638ba51b7c2e88bc0 Link: http://lkml.kernel.org/r/1486050195-18629-1-git-send-email-dave@stgolabs.net Signed-off-by: Davidlohr Bueso Reported-by: Gareth Evans Cc: Manfred Spraul Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds [bwh: Backported to 3.2: use SHMLBA constant instead of shmlba parameter] Signed-off-by: Ben Hutchings CVE-2017-5669 Signed-off-by: Kevin F. Haggerty --- ipc/shm.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ipc/shm.c b/ipc/shm.c index 634b0ba15f4..b4ac3dc95e0 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -965,8 +965,13 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) goto out; else if ((addr = (ulong)shmaddr)) { if (addr & (SHMLBA-1)) { - if (shmflg & SHM_RND) - addr &= ~(SHMLBA-1); /* round down */ + /* + * Round down to the nearest multiple of shmlba. + * For sane do_mmap_pgoff() parameters, avoid + * round downs that trigger nil-page and MAP_FIXED. + */ + if ((shmflg & SHM_RND) && addr >= SHMLBA) + addr &= ~(SHMLBA - 1); else #ifndef __ARCH_FORCE_SHMLBA if (addr & ~PAGE_MASK)