Commit 6a424eda authored by Marcelo Tosatti's avatar Marcelo Tosatti Committed by Linus Torvalds

[PATCH] do_brk() needs mmap_sem write-locked

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.
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent fa6e49a2
...@@ -127,7 +127,9 @@ static void set_brk(unsigned long start, unsigned long end) ...@@ -127,7 +127,9 @@ static void set_brk(unsigned long start, unsigned long end)
end = PAGE_ALIGN(end); end = PAGE_ALIGN(end);
if (end <= start) if (end <= start)
return; return;
down_write(&current->mm->mmap_sem);
do_brk(start, end - start); do_brk(start, end - start);
up_write(&current->mm->mmap_sem);
} }
...@@ -375,7 +377,9 @@ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex, ...@@ -375,7 +377,9 @@ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex,
/* Map the last of the bss segment */ /* Map the last of the bss segment */
if (last_bss > len) { if (last_bss > len) {
down_write(&current->mm->mmap_sem);
do_brk(len, (last_bss - len)); do_brk(len, (last_bss - len));
up_write(&current->mm->mmap_sem);
} }
kfree(elf_phdata); kfree(elf_phdata);
...@@ -562,7 +566,9 @@ void irix_map_prda_page (void) ...@@ -562,7 +566,9 @@ void irix_map_prda_page (void)
unsigned long v; unsigned long v;
struct prda *pp; struct prda *pp;
down_write(&current->mm->mmap_sem);
v = do_brk (PRDA_ADDRESS, PAGE_SIZE); v = do_brk (PRDA_ADDRESS, PAGE_SIZE);
up_write(&current->mm->mmap_sem);
if (v < 0) if (v < 0)
return; return;
...@@ -852,8 +858,11 @@ static int load_irix_library(struct file *file) ...@@ -852,8 +858,11 @@ static int load_irix_library(struct file *file)
len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+ 0xfff) & 0xfffff000; len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+ 0xfff) & 0xfffff000;
bss = elf_phdata->p_memsz + elf_phdata->p_vaddr; bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
if (bss > len) if (bss > len) {
down_write(&current->mm->mmap_sem);
do_brk(len, bss-len); do_brk(len, bss-len);
up_write(&current->mm->mmap_sem);
}
kfree(elf_phdata); kfree(elf_phdata);
return 0; return 0;
} }
......
...@@ -49,7 +49,9 @@ static void set_brk(unsigned long start, unsigned long end) ...@@ -49,7 +49,9 @@ static void set_brk(unsigned long start, unsigned long end)
end = PAGE_ALIGN(end); end = PAGE_ALIGN(end);
if (end <= start) if (end <= start)
return; return;
down_write(&current->mm->mmap_sem);
do_brk(start, end - start); do_brk(start, end - start);
up_write(&current->mm->mmap_sem);
} }
/* /*
...@@ -246,10 +248,14 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -246,10 +248,14 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if (N_MAGIC(ex) == NMAGIC) { if (N_MAGIC(ex) == NMAGIC) {
loff_t pos = fd_offset; loff_t pos = fd_offset;
/* Fuck me plenty... */ /* Fuck me plenty... */
down_write(&current->mm->mmap_sem);
error = do_brk(N_TXTADDR(ex), ex.a_text); error = do_brk(N_TXTADDR(ex), ex.a_text);
up_write(&current->mm->mmap_sem);
bprm->file->f_op->read(bprm->file, (char __user *)N_TXTADDR(ex), bprm->file->f_op->read(bprm->file, (char __user *)N_TXTADDR(ex),
ex.a_text, &pos); ex.a_text, &pos);
down_write(&current->mm->mmap_sem);
error = do_brk(N_DATADDR(ex), ex.a_data); error = do_brk(N_DATADDR(ex), ex.a_data);
up_write(&current->mm->mmap_sem);
bprm->file->f_op->read(bprm->file, (char __user *)N_DATADDR(ex), bprm->file->f_op->read(bprm->file, (char __user *)N_DATADDR(ex),
ex.a_data, &pos); ex.a_data, &pos);
goto beyond_if; goto beyond_if;
...@@ -257,8 +263,10 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -257,8 +263,10 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if (N_MAGIC(ex) == OMAGIC) { if (N_MAGIC(ex) == OMAGIC) {
loff_t pos = fd_offset; loff_t pos = fd_offset;
down_write(&current->mm->mmap_sem);
do_brk(N_TXTADDR(ex) & PAGE_MASK, do_brk(N_TXTADDR(ex) & PAGE_MASK,
ex.a_text+ex.a_data + PAGE_SIZE - 1); ex.a_text+ex.a_data + PAGE_SIZE - 1);
up_write(&current->mm->mmap_sem);
bprm->file->f_op->read(bprm->file, (char __user *)N_TXTADDR(ex), bprm->file->f_op->read(bprm->file, (char __user *)N_TXTADDR(ex),
ex.a_text+ex.a_data, &pos); ex.a_text+ex.a_data, &pos);
} else { } else {
...@@ -272,7 +280,9 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -272,7 +280,9 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if (!bprm->file->f_op->mmap) { if (!bprm->file->f_op->mmap) {
loff_t pos = fd_offset; loff_t pos = fd_offset;
down_write(&current->mm->mmap_sem);
do_brk(0, ex.a_text+ex.a_data); do_brk(0, ex.a_text+ex.a_data);
up_write(&current->mm->mmap_sem);
bprm->file->f_op->read(bprm->file, bprm->file->f_op->read(bprm->file,
(char __user *)N_TXTADDR(ex), (char __user *)N_TXTADDR(ex),
ex.a_text+ex.a_data, &pos); ex.a_text+ex.a_data, &pos);
...@@ -389,7 +399,9 @@ static int load_aout32_library(struct file *file) ...@@ -389,7 +399,9 @@ static int load_aout32_library(struct file *file)
len = PAGE_ALIGN(ex.a_text + ex.a_data); len = PAGE_ALIGN(ex.a_text + ex.a_data);
bss = ex.a_text + ex.a_data + ex.a_bss; bss = ex.a_text + ex.a_data + ex.a_bss;
if (bss > len) { if (bss > len) {
down_write(&current->mm->mmap_sem);
error = do_brk(start_addr + len, bss - len); error = do_brk(start_addr + len, bss - len);
up_write(&current->mm->mmap_sem);
retval = error; retval = error;
if (error != start_addr + len) if (error != start_addr + len)
goto out; goto out;
......
...@@ -115,7 +115,9 @@ static void set_brk(unsigned long start, unsigned long end) ...@@ -115,7 +115,9 @@ static void set_brk(unsigned long start, unsigned long end)
end = PAGE_ALIGN(end); end = PAGE_ALIGN(end);
if (end <= start) if (end <= start)
return; return;
down_write(&current->mm->mmap_sem);
do_brk(start, end - start); do_brk(start, end - start);
up_write(&current->mm->mmap_sem);
} }
#if CORE_DUMP #if CORE_DUMP
...@@ -325,7 +327,10 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -325,7 +327,10 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
pos = 32; pos = 32;
map_size = ex.a_text+ex.a_data; map_size = ex.a_text+ex.a_data;
down_write(&current->mm->mmap_sem);
error = do_brk(text_addr & PAGE_MASK, map_size); error = do_brk(text_addr & PAGE_MASK, map_size);
up_write(&current->mm->mmap_sem);
if (error != (text_addr & PAGE_MASK)) { if (error != (text_addr & PAGE_MASK)) {
send_sig(SIGKILL, current, 0); send_sig(SIGKILL, current, 0);
return error; return error;
...@@ -361,7 +366,9 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -361,7 +366,9 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) { if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {
loff_t pos = fd_offset; loff_t pos = fd_offset;
down_write(&current->mm->mmap_sem);
do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data); do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
up_write(&current->mm->mmap_sem);
bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex), bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex),
ex.a_text+ex.a_data, &pos); ex.a_text+ex.a_data, &pos);
flush_icache_range((unsigned long) N_TXTADDR(ex), flush_icache_range((unsigned long) N_TXTADDR(ex),
...@@ -469,8 +476,9 @@ static int load_aout_library(struct file *file) ...@@ -469,8 +476,9 @@ static int load_aout_library(struct file *file)
error_time = jiffies; error_time = jiffies;
} }
#endif #endif
down_write(&current->mm->mmap_sem);
do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss); do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
up_write(&current->mm->mmap_sem);
file->f_op->read(file, (char *)start_addr, file->f_op->read(file, (char *)start_addr,
ex.a_text + ex.a_data, &pos); ex.a_text + ex.a_data, &pos);
...@@ -494,7 +502,9 @@ static int load_aout_library(struct file *file) ...@@ -494,7 +502,9 @@ static int load_aout_library(struct file *file)
len = PAGE_ALIGN(ex.a_text + ex.a_data); len = PAGE_ALIGN(ex.a_text + ex.a_data);
bss = ex.a_text + ex.a_data + ex.a_bss; bss = ex.a_text + ex.a_data + ex.a_bss;
if (bss > len) { if (bss > len) {
down_write(&current->mm->mmap_sem);
error = do_brk(start_addr + len, bss - len); error = do_brk(start_addr + len, bss - len);
up_write(&current->mm->mmap_sem);
retval = error; retval = error;
if (error != start_addr + len) if (error != start_addr + len)
goto out; goto out;
......
...@@ -50,7 +50,10 @@ static int set_brk(unsigned long start, unsigned long end) ...@@ -50,7 +50,10 @@ static int set_brk(unsigned long start, unsigned long end)
start = PAGE_ALIGN(start); start = PAGE_ALIGN(start);
end = PAGE_ALIGN(end); end = PAGE_ALIGN(end);
if (end > start) { if (end > start) {
unsigned long addr = do_brk(start, end - start); unsigned long addr;
down_write(&current->mm->mmap_sem);
addr = do_brk(start, end - start);
up_write(&current->mm->mmap_sem);
if (BAD_ADDR(addr)) if (BAD_ADDR(addr))
return addr; return addr;
} }
...@@ -323,10 +326,14 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -323,10 +326,14 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
loff_t pos = fd_offset; loff_t pos = fd_offset;
/* Fuck me plenty... */ /* Fuck me plenty... */
/* <AOL></AOL> */ /* <AOL></AOL> */
down_write(&current->mm->mmap_sem);
error = do_brk(N_TXTADDR(ex), ex.a_text); error = do_brk(N_TXTADDR(ex), ex.a_text);
up_write(&current->mm->mmap_sem);
bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex), bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
ex.a_text, &pos); ex.a_text, &pos);
down_write(&current->mm->mmap_sem);
error = do_brk(N_DATADDR(ex), ex.a_data); error = do_brk(N_DATADDR(ex), ex.a_data);
up_write(&current->mm->mmap_sem);
bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex), bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),
ex.a_data, &pos); ex.a_data, &pos);
goto beyond_if; goto beyond_if;
...@@ -346,8 +353,9 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -346,8 +353,9 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
pos = 32; pos = 32;
map_size = ex.a_text+ex.a_data; map_size = ex.a_text+ex.a_data;
#endif #endif
down_write(&current->mm->mmap_sem);
error = do_brk(text_addr & PAGE_MASK, map_size); error = do_brk(text_addr & PAGE_MASK, map_size);
up_write(&current->mm->mmap_sem);
if (error != (text_addr & PAGE_MASK)) { if (error != (text_addr & PAGE_MASK)) {
send_sig(SIGKILL, current, 0); send_sig(SIGKILL, current, 0);
return error; return error;
...@@ -382,7 +390,9 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -382,7 +390,9 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) { if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {
loff_t pos = fd_offset; loff_t pos = fd_offset;
down_write(&current->mm->mmap_sem);
do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data); do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
up_write(&current->mm->mmap_sem);
bprm->file->f_op->read(bprm->file, bprm->file->f_op->read(bprm->file,
(char __user *)N_TXTADDR(ex), (char __user *)N_TXTADDR(ex),
ex.a_text+ex.a_data, &pos); ex.a_text+ex.a_data, &pos);
...@@ -487,8 +497,9 @@ static int load_aout_library(struct file *file) ...@@ -487,8 +497,9 @@ static int load_aout_library(struct file *file)
file->f_dentry->d_name.name); file->f_dentry->d_name.name);
error_time = jiffies; error_time = jiffies;
} }
down_write(&current->mm->mmap_sem);
do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss); do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
up_write(&current->mm->mmap_sem);
file->f_op->read(file, (char __user *)start_addr, file->f_op->read(file, (char __user *)start_addr,
ex.a_text + ex.a_data, &pos); ex.a_text + ex.a_data, &pos);
......
...@@ -88,7 +88,10 @@ static int set_brk(unsigned long start, unsigned long end) ...@@ -88,7 +88,10 @@ static int set_brk(unsigned long start, unsigned long end)
start = ELF_PAGEALIGN(start); start = ELF_PAGEALIGN(start);
end = ELF_PAGEALIGN(end); end = ELF_PAGEALIGN(end);
if (end > start) { if (end > start) {
unsigned long addr = do_brk(start, end - start); unsigned long addr;
down_write(&current->mm->mmap_sem);
addr = do_brk(start, end - start);
up_write(&current->mm->mmap_sem);
if (BAD_ADDR(addr)) if (BAD_ADDR(addr))
return addr; return addr;
} }
...@@ -409,7 +412,9 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, ...@@ -409,7 +412,9 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
/* Map the last of the bss segment */ /* Map the last of the bss segment */
if (last_bss > elf_bss) { if (last_bss > elf_bss) {
down_write(&current->mm->mmap_sem);
error = do_brk(elf_bss, last_bss - elf_bss); error = do_brk(elf_bss, last_bss - elf_bss);
up_write(&current->mm->mmap_sem);
if (BAD_ADDR(error)) if (BAD_ADDR(error))
goto out_close; goto out_close;
} }
...@@ -449,7 +454,9 @@ static unsigned long load_aout_interp(struct exec * interp_ex, ...@@ -449,7 +454,9 @@ static unsigned long load_aout_interp(struct exec * interp_ex,
goto out; goto out;
} }
down_write(&current->mm->mmap_sem);
do_brk(0, text_data); do_brk(0, text_data);
up_write(&current->mm->mmap_sem);
if (!interpreter->f_op || !interpreter->f_op->read) if (!interpreter->f_op || !interpreter->f_op->read)
goto out; goto out;
if (interpreter->f_op->read(interpreter, addr, text_data, &offset) < 0) if (interpreter->f_op->read(interpreter, addr, text_data, &offset) < 0)
...@@ -457,8 +464,11 @@ static unsigned long load_aout_interp(struct exec * interp_ex, ...@@ -457,8 +464,11 @@ static unsigned long load_aout_interp(struct exec * interp_ex,
flush_icache_range((unsigned long)addr, flush_icache_range((unsigned long)addr,
(unsigned long)addr + text_data); (unsigned long)addr + text_data);
down_write(&current->mm->mmap_sem);
do_brk(ELF_PAGESTART(text_data + ELF_MIN_ALIGN - 1), do_brk(ELF_PAGESTART(text_data + ELF_MIN_ALIGN - 1),
interp_ex->a_bss); interp_ex->a_bss);
up_write(&current->mm->mmap_sem);
elf_entry = interp_ex->a_entry; elf_entry = interp_ex->a_entry;
out: out:
......
...@@ -1902,6 +1902,12 @@ unsigned long do_brk(unsigned long addr, unsigned long len) ...@@ -1902,6 +1902,12 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
return -EAGAIN; return -EAGAIN;
} }
/*
* 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 * Clear old maps. this also does some error checking for us
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment