do_brk() needs mmap_sem write-locked
Subsystems (5)
PATCH 1/1 do_brk() needs mmap_sem write-locked
Hi Linus,
It seems to be general consensus that its safer to require all do_brk() callers to grab mmap_sem, and have do_brk to warn otherwise. This is what the following patch does.
Similar version has been changed to in v2.4.
Please apply
arch/mips/kernel/irixelf.c +10 -1
@@ -127,7 +127,9 @@ end =PAGE_ALIGN (end);if (end <= start)return ; +down_write (¤t->mm->mmap_sem);do_brk (start, end - start); +up_write (¤t->mm->mmap_sem); }
@@ -375,7 +377,9 @@/* Map the last of the bss segment */ if (last_bss > len) { +down_write (¤t->mm->mmap_sem);do_brk (len, (last_bss - len)); +up_write (¤t->mm->mmap_sem); }kfree (elf_phdata);
@@ -562,7 +566,9 @@unsigned long v;struct prda *pp; +down_write (¤t->mm->mmap_sem); v =do_brk (PRDA_ADDRESS ,PAGE_SIZE ); +up_write (¤t->mm->mmap_sem);if (v <0 )return ;
@@ -852,8 +858,11 @@ len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+0xfff ) &0xfffff000 ; bss = elf_phdata->p_memsz + elf_phdata->p_vaddr; -if (bss > len) +if (bss > len) { +down_write (¤t->mm->mmap_sem);do_brk (len, bss-len); +up_write (¤t->mm->mmap_sem); + }kfree (elf_phdata);return 0 ; }
arch/sparc64/kernel/binfmt_aout32.c +12 -0
@@ -49,7 +49,9 @@ end =PAGE_ALIGN (end);if (end <= start)return ; +down_write (¤t->mm->mmap_sem);do_brk (start, end - start); +up_write (¤t->mm->mmap_sem); }/*
@@ -246,10 +248,14 @@if (N_MAGIC (ex) ==NMAGIC ) {loff_t pos = fd_offset;/* Fuck me plenty... */ +down_write (¤t->mm->mmap_sem); error =do_brk (N_TXTADDR (ex), ex.a_text); +up_write (¤t->mm->mmap_sem); bprm->file->f_op->read (bprm->file, (char __user *)N_TXTADDR (ex), ex.a_text, &pos); +down_write (¤t->mm->mmap_sem); error =do_brk (N_DATADDR (ex), ex.a_data); +up_write (¤t->mm->mmap_sem); bprm->file->f_op->read (bprm->file, (char __user *)N_DATADDR (ex), ex.a_data, &pos); goto beyond_if;
@@ -257,8 +263,10 @@if (N_MAGIC (ex) ==OMAGIC ) {loff_t pos = fd_offset; +down_write (¤t->mm->mmap_sem);do_brk (N_TXTADDR (ex) &PAGE_MASK , ex.a_text+ex.a_data +PAGE_SIZE -1 ); +up_write (¤t->mm->mmap_sem); bprm->file->f_op->read (bprm->file, (char __user *)N_TXTADDR (ex), ex.a_text+ex.a_data, &pos); }else {
@@ -272,7 +280,9 @@if (!bprm->file->f_op->mmap) {loff_t pos = fd_offset; +down_write (¤t->mm->mmap_sem);do_brk (0 , ex.a_text+ex.a_data); +up_write (¤t->mm->mmap_sem); bprm->file->f_op->read (bprm->file, (char __user *)N_TXTADDR (ex), ex.a_text+ex.a_data, &pos);
@@ -389,7 +399,9 @@ len =PAGE_ALIGN (ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss;if (bss > len) { +down_write (¤t->mm->mmap_sem); error =do_brk (start_addr + len, bss - len); +up_write (¤t->mm->mmap_sem); retval = error;if (error != start_addr + len) goto out;
arch/x86_64/ia32/ia32_aout.c +11 -1
@@ -115,7 +115,9 @@ end =PAGE_ALIGN (end);if (end <= start)return ; +down_write (¤t->mm->mmap_sem);do_brk (start, end - start); +up_write (¤t->mm->mmap_sem); }#if CORE_DUMP
@@ -325,7 +327,10 @@ pos =32 ; map_size = ex.a_text+ex.a_data; +down_write (¤t->mm->mmap_sem); error =do_brk (text_addr &PAGE_MASK , map_size); +up_write (¤t->mm->mmap_sem); +if (error != (text_addr &PAGE_MASK )) {send_sig (SIGKILL , current,0 );return error;
@@ -361,7 +366,9 @@if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK ) !=0 )) {loff_t pos = fd_offset; +down_write (¤t->mm->mmap_sem);do_brk (N_TXTADDR (ex), ex.a_text+ex.a_data); +up_write (¤t->mm->mmap_sem); bprm->file->f_op->read (bprm->file,(char *)N_TXTADDR (ex), ex.a_text+ex.a_data, &pos); flush_icache_range((unsigned long) N_TXTADDR(ex),
@@ -469,8 +476,9 @@ error_time = jiffies; }#endif - +down_write (¤t->mm->mmap_sem);do_brk (start_addr, ex.a_text + ex.a_data + ex.a_bss); +up_write (¤t->mm->mmap_sem); file->f_op->read (file, (char *)start_addr, ex.a_text + ex.a_data, &pos);
@@ -494,7 +502,9 @@ len =PAGE_ALIGN (ex.a_text + ex.a_data); bss = ex.a_text + ex.a_data + ex.a_bss;if (bss > len) { +down_write (¤t->mm->mmap_sem); error =do_brk (start_addr + len, bss - len); +up_write (¤t->mm->mmap_sem); retval = error;if (error != start_addr + len) goto out;
fs/binfmt_aout.c +14 -3
@@ -50,7 +50,10 @@ start =PAGE_ALIGN (start); end =PAGE_ALIGN (end);if (end > start) { -unsigned long addr =do_brk (start, end - start); +unsigned long addr; +down_write (¤t->mm->mmap_sem); + addr =do_brk (start, end - start); +up_write (¤t->mm->mmap_sem);if (BAD_ADDR (addr))return addr; }
@@ -323,10 +326,14 @@loff_t pos = fd_offset;/* Fuck me plenty... */ /* <AOL></AOL> */ +down_write (¤t->mm->mmap_sem); error =do_brk (N_TXTADDR (ex), ex.a_text); +up_write (¤t->mm->mmap_sem); bprm->file->f_op->read (bprm->file, (char *)N_TXTADDR (ex), ex.a_text, &pos); +down_write (¤t->mm->mmap_sem); error =do_brk (N_DATADDR (ex), ex.a_data); +up_write (¤t->mm->mmap_sem); bprm->file->f_op->read (bprm->file, (char *)N_DATADDR (ex), ex.a_data, &pos); goto beyond_if;
@@ -346,8 +353,9 @@ pos =32 ; map_size = ex.a_text+ex.a_data;#endif - +down_write (¤t->mm->mmap_sem); error =do_brk (text_addr &PAGE_MASK , map_size); +up_write (¤t->mm->mmap_sem);if (error != (text_addr &PAGE_MASK )) {send_sig (SIGKILL , current,0 );return error;
@@ -382,7 +390,9 @@if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK ) !=0 )) {loff_t pos = fd_offset; +down_write (¤t->mm->mmap_sem);do_brk (N_TXTADDR (ex), ex.a_text+ex.a_data); +up_write (¤t->mm->mmap_sem); bprm->file->f_op->read (bprm->file, (char __user *)N_TXTADDR (ex), ex.a_text+ex.a_data, &pos);
@@ -487,8 +497,9 @@ file->f_dentry->d_name.name); error_time = jiffies; } - +down_write (¤t->mm->mmap_sem);do_brk (start_addr, ex.a_text + ex.a_data + ex.a_bss); +up_write (¤t->mm->mmap_sem); file->f_op->read (file, (char __user *)start_addr, ex.a_text + ex.a_data, &pos);
fs/binfmt_elf.c +11 -1
@@ -88,7 +88,10 @@ start =ELF_PAGEALIGN (start); end =ELF_PAGEALIGN (end);if (end > start) { -unsigned long addr =do_brk (start, end - start); +unsigned long addr; +down_write (¤t->mm->mmap_sem); + addr =do_brk (start, end - start); +up_write (¤t->mm->mmap_sem);if (BAD_ADDR (addr))return addr; }
@@ -408,7 +411,9 @@/* Map the last of the bss segment */ if (last_bss > elf_bss) { +down_write (¤t->mm->mmap_sem); error =do_brk (elf_bss, last_bss - elf_bss); +up_write (¤t->mm->mmap_sem);if (BAD_ADDR (error)) goto out_close; }
@@ -448,7 +453,9 @@ goto out; } +down_write (¤t->mm->mmap_sem);do_brk (0 , text_data); +up_write (¤t->mm->mmap_sem);if (!interpreter->f_op || !interpreter->f_op->read) goto out;if (interpreter->f_op->read (interpreter, addr, text_data, &offset) <0 )
@@ -456,8 +463,11 @@flush_icache_range ((unsigned long )addr, (unsigned long )addr + text_data); + +down_write (¤t->mm->mmap_sem);do_brk (ELF_PAGESTART (text_data +ELF_MIN_ALIGN -1 ), interp_ex->a_bss); +up_write (¤t->mm->mmap_sem); elf_entry = interp_ex->a_entry; out:
mm/mmap.c +6 -0
@@ -1891,6 +1891,12 @@ }/* +* mm->mmap_sem is required to protect against another thread +* changing the mappings in case we sleep. +*/ +WARN_ON (down_read_trylock (&mm->mmap_sem)); + +/* * Clear old maps. this also does some error checking for us */ munmap_back:
Looks good, except for a small nit:
Quoted 7 lines · Marcelo Tosatti
diff -Nur
linux-2.6-curr.orig/mm/mmap.clinux-2.6-curr/mm/mmap.cmm/mmap.c +0
-0@@ -1891,6 +1891,12 @@ }
mm/mmap.c line 1902
Quoted 10 lines
mm/mmap.c +6
-0/* +* mm->mmap_sem is required to protect against another thread +* changing the mappings in case we sleep. +*/ +WARN_ON (down_read_trylock (&mm->mmap_sem)); + +/* * Clear old maps. this also does some error checking for us */ munmap_back:
if that warning ever triggers, mmap_sem will now be locked, and that will cause problems. So I suspect it's better to do
if (down_read_trylock(&mm->mmap_sem)) {
WARN_ON(1);
up_read(&mm->mmap_sem);
}and move that into a helper function of its own (something like "verify_mmap_write_lock_held()").
Linus
Quoted 26 lines · Linus Torvalds
Looks good, except for a small nit:
Quoted 7 lines · Marcelo Tosatti
diff -Nur
linux-2.6-curr.orig/mm/mmap.clinux-2.6-curr/mm/mmap.cmm/mmap.c +0
-0@@ -1891,6 +1891,12 @@ }mm/mmap.c line 1902
Quoted 10 lines
mm/mmap.c +6
-0/* +* mm->mmap_sem is required to protect against another thread +* changing the mappings in case we sleep. +*/ +WARN_ON (down_read_trylock (&mm->mmap_sem)); + +/* * Clear old maps. this also does some error checking for us */ munmap_back:if that warning ever triggers,
mmap_semwill now be locked, and that will cause problems. So I suspect it's better to do
Quoted 7 lines
if (down_read_trylock(&mm->mmap_sem)) { WARN_ON(1); up_read(&mm->mmap_sem); }and move that into a helper function of its own (something like "
verify_mmap_write_lock_held()").
OK - I've seen you just committed a fix.
I've fixed v2.4 version.
Quoted 6 lines · Linus Torvalds
if that warning ever triggers,
mmap_semwill now be locked, and that will cause problems. So I suspect it's better to doif (down_read_trylock(&mm->mmap_sem)) { WARN_ON(1); up_read(&mm->mmap_sem);
Better to leave the lock held and remember the error then drop it at the end - anything else means the WARN_ON case is a security hole.