diff mbox

kernel BUG at mm/gup.c:LINE!

Message ID 20180706053545.GD32658@dhcp22.suse.cz (mailing list archive)
State New, archived
Headers show

Commit Message

Michal Hocko July 6, 2018, 5:35 a.m. UTC
On Thu 05-07-18 14:30:17, Oscar Salvador wrote:
> On Thu, Jul 05, 2018 at 09:18:39AM +0200, Oscar Salvador wrote:
> >  
> > > This is more than unexpected. The patch merely move the alignment check
> > > up. I will try to investigate some more but I am off for next four days
> > > and won't be online most of the time.
> > > 
> > > Btw. does the same happen if you keep do_brk helper and add the length
> > > sanitization there as well?
> 
> I took another look.
> The problem was that while deleting the check in do_brk_flags(), this left then "len"
> local variable with an unset value, but we need it to contain the request value
> because we do use it in further calls in do_brk_flags(), like:

Very well spotted. Thanks for noticing! I am really half online so I
cannot give it much testing right now. But here is the updated patch
with the changelog. I cannot really tell whether the other change to
align up in load_elf_library is correct as well. It seems OK but
everything around elf loading is tricky from my past experience.

My patch simply makes vm_brk_flags behavior consistent so I believe we
should do that regardless (unless I've screwed something else here of
course).


From bbe0891cc01425bd99643acd8d9afa7672dc29da Mon Sep 17 00:00:00 2001
From: Michal Hocko <mhocko@suse.com>
Date: Wed, 4 Jul 2018 15:16:54 +0200
Subject: [PATCH] mm: do not bug_on on incorrect lenght in __mm_populate

syzbot has noticed that a specially crafted library can easily hit
VM_BUG_ON in __mm_populate
localhost login: [   81.210241] emacs (9634) used greatest stack depth: 10416 bytes left
[  140.099935] ------------[ cut here ]------------
[  140.101904] kernel BUG at mm/gup.c:1242!
[  140.103572] invalid opcode: 0000 [#1] SMP
[  140.105220] CPU: 2 PID: 9667 Comm: a.out Not tainted 4.18.0-rc3 #644
[  140.107762] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 05/19/2017
[  140.112000] RIP: 0010:__mm_populate+0x1e2/0x1f0
[  140.113875] 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
[  140.121403] RSP: 0018:ffffc90000dffd78 EFLAGS: 00010293
[  140.123516] RAX: ffff8801366c63c0 RBX: 000000007bf81000 RCX: ffffffff813e4ee2
[  140.126352] RDX: 0000000000000000 RSI: 0000000000007676 RDI: 000000007bf81000
[  140.129236] RBP: ffffc90000dffdc0 R08: 0000000000000000 R09: 0000000000000000
[  140.132110] R10: ffff880135895c80 R11: 0000000000000000 R12: 0000000000007676
[  140.134955] R13: 0000000000008000 R14: 0000000000000000 R15: 0000000000007676
[  140.137785] FS:  0000000000000000(0000) GS:ffff88013a680000(0063) knlGS:00000000f7db9700
[  140.140998] CS:  0010 DS: 002b ES: 002b CR0: 0000000080050033
[  140.143303] CR2: 00000000f7ea56e0 CR3: 0000000134674004 CR4: 00000000000606e0
[  140.145906] Call Trace:
[  140.146728]  vm_brk_flags+0xc3/0x100
[  140.147830]  vm_brk+0x1f/0x30
[  140.148714]  load_elf_library+0x281/0x2e0
[  140.149875]  __ia32_sys_uselib+0x170/0x1e0
[  140.151028]  ? copy_overflow+0x30/0x30
[  140.152105]  ? __ia32_sys_uselib+0x170/0x1e0
[  140.153301]  do_fast_syscall_32+0xca/0x420
[  140.154455]  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.

Reported-by: syzbot <syzbot+5dcb560fe12aa5091c06@syzkaller.appspotmail.com>
[osalvador: fix up vm_brk_flags s@request@len@]
Tested-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Cc: stable
Signed-off-by: Michal Hocko <mhocko@suse.com>
---
 mm/gup.c  |  2 --
 mm/mmap.c | 28 ++++++++++++----------------
 2 files changed, 12 insertions(+), 18 deletions(-)

Comments

Oscar Salvador July 6, 2018, 7:40 a.m. UTC | #1
> Reported-by: syzbot <syzbot+5dcb560fe12aa5091c06@syzkaller.appspotmail.com>
> [osalvador: fix up vm_brk_flags s@request@len@]
> Tested-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Cc: stable
> Signed-off-by: Michal Hocko <mhocko@suse.com>

hi Michal,

I gave it another spin and it works for me.

FWIW:
Reviewed-by: Oscar Salvador <osalvador@suse.de>
kernel test robot July 6, 2018, 7:50 a.m. UTC | #2
Hi Michal,

I love your patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.18-rc3 next-20180705]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Michal-Hocko/mm-do-not-bug_on-on-incorrect-lenght-in-__mm_populate/20180706-134850
config: x86_64-randconfig-x015-201826 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

   mm/mmap.c: In function 'do_brk_flags':
>> mm/mmap.c:2936:16: error: 'len' redeclared as different kind of symbol
     unsigned long len;
                   ^~~
   mm/mmap.c:2932:59: note: previous definition of 'len' was here
    static int do_brk_flags(unsigned long addr, unsigned long len, unsigned long flags, struct list_head *uf)
                                                              ^~~

vim +/len +2936 mm/mmap.c

^1da177e4 Linus Torvalds        2005-04-16  2926  
^1da177e4 Linus Torvalds        2005-04-16  2927  /*
^1da177e4 Linus Torvalds        2005-04-16  2928   *  this is really a simplified "do_mmap".  it only handles
^1da177e4 Linus Torvalds        2005-04-16  2929   *  anonymous maps.  eventually we may be able to do some
^1da177e4 Linus Torvalds        2005-04-16  2930   *  brk-specific accounting here.
^1da177e4 Linus Torvalds        2005-04-16  2931   */
e3049e198 Michal Hocko          2018-07-06  2932  static int do_brk_flags(unsigned long addr, unsigned long len, unsigned long flags, struct list_head *uf)
^1da177e4 Linus Torvalds        2005-04-16  2933  {
^1da177e4 Linus Torvalds        2005-04-16  2934  	struct mm_struct *mm = current->mm;
^1da177e4 Linus Torvalds        2005-04-16  2935  	struct vm_area_struct *vma, *prev;
16e72e9b3 Denys Vlasenko        2017-02-22 @2936  	unsigned long len;
^1da177e4 Linus Torvalds        2005-04-16  2937  	struct rb_node **rb_link, *rb_parent;
^1da177e4 Linus Torvalds        2005-04-16  2938  	pgoff_t pgoff = addr >> PAGE_SHIFT;
3a4597568 Kirill Korotaev       2006-09-07  2939  	int error;
^1da177e4 Linus Torvalds        2005-04-16  2940  
16e72e9b3 Denys Vlasenko        2017-02-22  2941  	/* Until we need other flags, refuse anything except VM_EXEC. */
16e72e9b3 Denys Vlasenko        2017-02-22  2942  	if ((flags & (~VM_EXEC)) != 0)
16e72e9b3 Denys Vlasenko        2017-02-22  2943  		return -EINVAL;
16e72e9b3 Denys Vlasenko        2017-02-22  2944  	flags |= VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
3a4597568 Kirill Korotaev       2006-09-07  2945  
2c6a10161 Al Viro               2009-12-03  2946  	error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED);
de1741a13 Alexander Kuleshov    2015-11-05  2947  	if (offset_in_page(error))
3a4597568 Kirill Korotaev       2006-09-07  2948  		return error;
3a4597568 Kirill Korotaev       2006-09-07  2949  
363ee17f0 Davidlohr Bueso       2014-01-21  2950  	error = mlock_future_check(mm, mm->def_flags, len);
363ee17f0 Davidlohr Bueso       2014-01-21  2951  	if (error)
363ee17f0 Davidlohr Bueso       2014-01-21  2952  		return error;
^1da177e4 Linus Torvalds        2005-04-16  2953  
^1da177e4 Linus Torvalds        2005-04-16  2954  	/*
^1da177e4 Linus Torvalds        2005-04-16  2955  	 * mm->mmap_sem is required to protect against another thread
^1da177e4 Linus Torvalds        2005-04-16  2956  	 * changing the mappings in case we sleep.
^1da177e4 Linus Torvalds        2005-04-16  2957  	 */
^1da177e4 Linus Torvalds        2005-04-16  2958  	verify_mm_writelocked(mm);
^1da177e4 Linus Torvalds        2005-04-16  2959  
^1da177e4 Linus Torvalds        2005-04-16  2960  	/*
^1da177e4 Linus Torvalds        2005-04-16  2961  	 * Clear old maps.  this also does some error checking for us
^1da177e4 Linus Torvalds        2005-04-16  2962  	 */
9fcd14571 Rasmus Villemoes      2015-04-15  2963  	while (find_vma_links(mm, addr, addr + len, &prev, &rb_link,
9fcd14571 Rasmus Villemoes      2015-04-15  2964  			      &rb_parent)) {
897ab3e0c Mike Rapoport         2017-02-24  2965  		if (do_munmap(mm, addr, len, uf))
^1da177e4 Linus Torvalds        2005-04-16  2966  			return -ENOMEM;
^1da177e4 Linus Torvalds        2005-04-16  2967  	}
^1da177e4 Linus Torvalds        2005-04-16  2968  
^1da177e4 Linus Torvalds        2005-04-16  2969  	/* Check against address space limits *after* clearing old maps... */
846383359 Konstantin Khlebnikov 2016-01-14  2970  	if (!may_expand_vm(mm, flags, len >> PAGE_SHIFT))
^1da177e4 Linus Torvalds        2005-04-16  2971  		return -ENOMEM;
^1da177e4 Linus Torvalds        2005-04-16  2972  
^1da177e4 Linus Torvalds        2005-04-16  2973  	if (mm->map_count > sysctl_max_map_count)
^1da177e4 Linus Torvalds        2005-04-16  2974  		return -ENOMEM;
^1da177e4 Linus Torvalds        2005-04-16  2975  
191c54244 Al Viro               2012-02-13  2976  	if (security_vm_enough_memory_mm(mm, len >> PAGE_SHIFT))
^1da177e4 Linus Torvalds        2005-04-16  2977  		return -ENOMEM;
^1da177e4 Linus Torvalds        2005-04-16  2978  
^1da177e4 Linus Torvalds        2005-04-16  2979  	/* Can we just expand an old private anonymous mapping? */
ba470de43 Rik van Riel          2008-10-18  2980  	vma = vma_merge(mm, prev, addr, addr + len, flags,
19a809afe Andrea Arcangeli      2015-09-04  2981  			NULL, NULL, pgoff, NULL, NULL_VM_UFFD_CTX);
ba470de43 Rik van Riel          2008-10-18  2982  	if (vma)
^1da177e4 Linus Torvalds        2005-04-16  2983  		goto out;
^1da177e4 Linus Torvalds        2005-04-16  2984  
^1da177e4 Linus Torvalds        2005-04-16  2985  	/*
^1da177e4 Linus Torvalds        2005-04-16  2986  	 * create a vma struct for an anonymous mapping
^1da177e4 Linus Torvalds        2005-04-16  2987  	 */
c5e3b83e9 Pekka Enberg          2006-03-25  2988  	vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
^1da177e4 Linus Torvalds        2005-04-16  2989  	if (!vma) {
^1da177e4 Linus Torvalds        2005-04-16  2990  		vm_unacct_memory(len >> PAGE_SHIFT);
^1da177e4 Linus Torvalds        2005-04-16  2991  		return -ENOMEM;
^1da177e4 Linus Torvalds        2005-04-16  2992  	}
^1da177e4 Linus Torvalds        2005-04-16  2993  
5beb49305 Rik van Riel          2010-03-05  2994  	INIT_LIST_HEAD(&vma->anon_vma_chain);
^1da177e4 Linus Torvalds        2005-04-16  2995  	vma->vm_mm = mm;
^1da177e4 Linus Torvalds        2005-04-16  2996  	vma->vm_start = addr;
^1da177e4 Linus Torvalds        2005-04-16  2997  	vma->vm_end = addr + len;
^1da177e4 Linus Torvalds        2005-04-16  2998  	vma->vm_pgoff = pgoff;
^1da177e4 Linus Torvalds        2005-04-16  2999  	vma->vm_flags = flags;
3ed75eb8f Coly Li               2007-10-18  3000  	vma->vm_page_prot = vm_get_page_prot(flags);
^1da177e4 Linus Torvalds        2005-04-16  3001  	vma_link(mm, vma, prev, rb_link, rb_parent);
^1da177e4 Linus Torvalds        2005-04-16  3002  out:
3af9e8592 Eric B Munson         2010-05-18  3003  	perf_event_mmap(vma);
^1da177e4 Linus Torvalds        2005-04-16  3004  	mm->total_vm += len >> PAGE_SHIFT;
846383359 Konstantin Khlebnikov 2016-01-14  3005  	mm->data_vm += len >> PAGE_SHIFT;
128557ffe Michel Lespinasse     2013-02-22  3006  	if (flags & VM_LOCKED)
ba470de43 Rik van Riel          2008-10-18  3007  		mm->locked_vm += (len >> PAGE_SHIFT);
d9104d1ca Cyrill Gorcunov       2013-09-11  3008  	vma->vm_flags |= VM_SOFTDIRTY;
5d22fc25d Linus Torvalds        2016-05-27  3009  	return 0;
^1da177e4 Linus Torvalds        2005-04-16  3010  }
^1da177e4 Linus Torvalds        2005-04-16  3011  

:::::: The code at line 2936 was first introduced by commit
:::::: 16e72e9b30986ee15f17fbb68189ca842c32af58 powerpc: do not make the entire heap executable

:::::: TO: Denys Vlasenko <dvlasenk@redhat.com>
:::::: CC: Linus Torvalds <torvalds@linux-foundation.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Oscar Salvador July 6, 2018, 8:23 a.m. UTC | #3
On Fri, Jul 06, 2018 at 03:50:53PM +0800, kbuild test robot wrote:
> Hi Michal,
> 
> I love your patch! Yet something to improve:
> 
> [auto build test ERROR on linus/master]
> [also build test ERROR on v4.18-rc3 next-20180705]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
> 
> url:    https://github.com/0day-ci/linux/commits/Michal-Hocko/mm-do-not-bug_on-on-incorrect-lenght-in-__mm_populate/20180706-134850
> config: x86_64-randconfig-x015-201826 (attached as .config)
> compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
> reproduce:
>         # save the attached .config to linux build tree
>         make ARCH=x86_64 
> 
> All errors (new ones prefixed by >>):
> 
>    mm/mmap.c: In function 'do_brk_flags':
> >> mm/mmap.c:2936:16: error: 'len' redeclared as different kind of symbol
>      unsigned long len;
>                    ^~~
>    mm/mmap.c:2932:59: note: previous definition of 'len' was here
>     static int do_brk_flags(unsigned long addr, unsigned long len, unsigned long flags, struct list_head *uf)

Somehow I missed that.
Maybe some remains from yesterday.

The local variable "len" must be dropped.
diff mbox

Patch

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)
 	int locked = 0;
 	long ret = 0;
 
-	VM_BUG_ON(start & ~PAGE_MASK);
-	VM_BUG_ON(len != PAGE_ALIGN(len));
 	end = start + len;
 
 	for (nstart = start; nstart < end; nstart = nend) {
diff --git a/mm/mmap.c b/mm/mmap.c
index d1eb87ef4b1a..9e68b7041ae3 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)
 	return next;
 }
 
-static int do_brk(unsigned long addr, unsigned long len, struct list_head *uf);
-
+static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long flags,
+		struct list_head *uf);
 SYSCALL_DEFINE1(brk, unsigned long, brk)
 {
 	unsigned long retval;
@@ -245,7 +245,7 @@  SYSCALL_DEFINE1(brk, unsigned long, brk)
 		goto out;
 
 	/* Ok, looks good - let it rip. */
-	if (do_brk(oldbrk, newbrk-oldbrk, &uf) < 0)
+	if (do_brk_flags(oldbrk, newbrk-oldbrk, 0, &uf) < 0)
 		goto out;
 
 set_brk:
@@ -2929,7 +2929,7 @@  static inline void verify_mm_writelocked(struct mm_struct *mm)
  *  anonymous maps.  eventually we may be able to do some
  *  brk-specific accounting here.
  */
-static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long flags, struct list_head *uf)
+static int do_brk_flags(unsigned long addr, unsigned long len, unsigned long flags, struct list_head *uf)
 {
 	struct mm_struct *mm = current->mm;
 	struct vm_area_struct *vma, *prev;
@@ -2938,12 +2938,6 @@  static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long
 	pgoff_t pgoff = addr >> PAGE_SHIFT;
 	int error;
 
-	len = PAGE_ALIGN(request);
-	if (len < request)
-		return -ENOMEM;
-	if (!len)
-		return 0;
-
 	/* Until we need other flags, refuse anything except VM_EXEC. */
 	if ((flags & (~VM_EXEC)) != 0)
 		return -EINVAL;
@@ -3015,18 +3009,20 @@  static int do_brk_flags(unsigned long addr, unsigned long request, unsigned long
 	return 0;
 }
 
-static int do_brk(unsigned long addr, unsigned long len, struct list_head *uf)
-{
-	return do_brk_flags(addr, len, 0, uf);
-}
-
-int vm_brk_flags(unsigned long addr, unsigned long len, unsigned long flags)
+int vm_brk_flags(unsigned long addr, unsigned long request, unsigned long flags)
 {
 	struct mm_struct *mm = current->mm;
+	unsigned long len;
 	int ret;
 	bool populate;
 	LIST_HEAD(uf);
 
+	len = PAGE_ALIGN(request);
+	if (len < request)
+		return -ENOMEM;
+	if (!len)
+		return 0;
+
 	if (down_write_killable(&mm->mmap_sem))
 		return -EINTR;