aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Hocko <mhocko@suse.com>2018-07-13 19:59:20 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-07-14 14:11:10 -0400
commitbb177a732c4369bb58a1fe1df8f552b6f0f7db5f (patch)
treeeb8fa192cc46ad130c10135c7c94f6b9945febab
parente3d301cae0092062cbcd6b4e7ceebbab9d87e263 (diff)
mm: do not bug_on on incorrect length in __mm_populate()
syzbot has noticed that a specially crafted library can easily hit VM_BUG_ON in __mm_populate kernel BUG at mm/gup.c:1242! invalid opcode: 0000 [#1] SMP CPU: 2 PID: 9667 Comm: a.out Not tainted 4.18.0-rc3 #644 Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 05/19/2017 RIP: 0010:__mm_populate+0x1e2/0x1f0 Code: 55 d0 65 48 33 14 25 28 00 00 00 89 d8 75 21 48 83 c4 20 5b 41 5c 41 5d 41 5e 41 5f 5d c3 e8 75 18 f1 ff 0f 0b e8 6e 18 f1 ff <0f> 0b 31 db eb c9 e8 93 06 e0 ff 0f 1f 00 55 48 89 e5 53 48 89 fb Call Trace: vm_brk_flags+0xc3/0x100 vm_brk+0x1f/0x30 load_elf_library+0x281/0x2e0 __ia32_sys_uselib+0x170/0x1e0 do_fast_syscall_32+0xca/0x420 entry_SYSENTER_compat+0x70/0x7f The reason is that the length of the new brk is not page aligned when we try to populate the it. There is no reason to bug on that though. do_brk_flags already aligns the length properly so the mapping is expanded as it should. All we need is to tell mm_populate about it. Besides that there is absolutely no reason to to bug_on in the first place. The worst thing that could happen is that the last page wouldn't get populated and that is far from putting system into an inconsistent state. Fix the issue by moving the length sanitization code from do_brk_flags up to vm_brk_flags. The only other caller of do_brk_flags is brk syscall entry and it makes sure to provide the proper length so t here is no need for sanitation and so we can use do_brk_flags without it. Also remove the bogus BUG_ONs. [osalvador@techadventures.net: fix up vm_brk_flags s@request@len@] Link: http://lkml.kernel.org/r/20180706090217.GI32658@dhcp22.suse.cz Signed-off-by: Michal Hocko <mhocko@suse.com> Reported-by: syzbot <syzbot+5dcb560fe12aa5091c06@syzkaller.appspotmail.com> Tested-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Reviewed-by: Oscar Salvador <osalvador@suse.de> Cc: Zi Yan <zi.yan@cs.rutgers.edu> Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com> Cc: Dan Williams <dan.j.williams@intel.com> Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> Cc: Michael S. Tsirkin <mst@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: "Huang, Ying" <ying.huang@intel.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/gup.c2
-rw-r--r--mm/mmap.c29
2 files changed, 12 insertions, 19 deletions
diff --git a/mm/gup.c b/mm/gup.c
index b70d7ba7cc13..fc5f98069f4e 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -1238,8 +1238,6 @@ int __mm_populate(unsigned long start, unsigned long len, int ignore_errors)
1238 int locked = 0; 1238 int locked = 0;
1239 long ret = 0; 1239 long ret = 0;
1240 1240
1241 VM_BUG_ON(start & ~PAGE_MASK);
1242 VM_BUG_ON(len != PAGE_ALIGN(len));
1243 end = start + len; 1241 end = start + len;
1244 1242
1245 for (nstart = start; nstart < end; nstart = nend) { 1243 for (nstart = start; nstart < end; nstart = nend) {
diff --git a/mm/mmap.c b/mm/mmap.c
index d1eb87ef4b1a..5801b5f0a634 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -186,8 +186,8 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma)
186 return next; 186 return next;
187} 187}
188 188
189static int do_brk(unsigned long addr, unsigned long len, struct list_head *uf); 189static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long flags,
190 190 struct list_head *uf);
191SYSCALL_DEFINE1(brk, unsigned long, brk) 191SYSCALL_DEFINE1(brk, unsigned long, brk)
192{ 192{
193 unsigned long retval; 193 unsigned long retval;
@@ -245,7 +245,7 @@ SYSCALL_DEFINE1(brk, unsigned long, brk)
245 goto out; 245 goto out;
246 246
247 /* Ok, looks good - let it rip. */ 247 /* Ok, looks good - let it rip. */
248 if (do_brk(oldbrk, newbrk-oldbrk, &uf) < 0) 248 if (do_brk_flags(oldbrk, newbrk-oldbrk, 0, &uf) < 0)
249 goto out; 249 goto out;
250 250
251set_brk: 251set_brk:
@@ -2929,21 +2929,14 @@ static inline void verify_mm_writelocked(struct mm_struct *mm)
2929 * anonymous maps. eventually we may be able to do some 2929 * anonymous maps. eventually we may be able to do some
2930 * brk-specific accounting here. 2930 * brk-specific accounting here.
2931 */ 2931 */
2932static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long flags, struct list_head *uf) 2932static int do_brk_flags(unsigned long addr, unsigned long len, unsigned long flags, struct list_head *uf)
2933{ 2933{
2934 struct mm_struct *mm = current->mm; 2934 struct mm_struct *mm = current->mm;
2935 struct vm_area_struct *vma, *prev; 2935 struct vm_area_struct *vma, *prev;
2936 unsigned long len;
2937 struct rb_node **rb_link, *rb_parent; 2936 struct rb_node **rb_link, *rb_parent;
2938 pgoff_t pgoff = addr >> PAGE_SHIFT; 2937 pgoff_t pgoff = addr >> PAGE_SHIFT;
2939 int error; 2938 int error;
2940 2939
2941 len = PAGE_ALIGN(request);
2942 if (len < request)
2943 return -ENOMEM;
2944 if (!len)
2945 return 0;
2946
2947 /* Until we need other flags, refuse anything except VM_EXEC. */ 2940 /* Until we need other flags, refuse anything except VM_EXEC. */
2948 if ((flags & (~VM_EXEC)) != 0) 2941 if ((flags & (~VM_EXEC)) != 0)
2949 return -EINVAL; 2942 return -EINVAL;
@@ -3015,18 +3008,20 @@ out:
3015 return 0; 3008 return 0;
3016} 3009}
3017 3010
3018static int do_brk(unsigned long addr, unsigned long len, struct list_head *uf) 3011int vm_brk_flags(unsigned long addr, unsigned long request, unsigned long flags)
3019{
3020 return do_brk_flags(addr, len, 0, uf);
3021}
3022
3023int vm_brk_flags(unsigned long addr, unsigned long len, unsigned long flags)
3024{ 3012{
3025 struct mm_struct *mm = current->mm; 3013 struct mm_struct *mm = current->mm;
3014 unsigned long len;
3026 int ret; 3015 int ret;
3027 bool populate; 3016 bool populate;
3028 LIST_HEAD(uf); 3017 LIST_HEAD(uf);
3029 3018
3019 len = PAGE_ALIGN(request);
3020 if (len < request)
3021 return -ENOMEM;
3022 if (!len)
3023 return 0;
3024
3030 if (down_write_killable(&mm->mmap_sem)) 3025 if (down_write_killable(&mm->mmap_sem))
3031 return -EINTR; 3026 return -EINTR;
3032 3027