
From: Julie DeWandel <jdewand@redhat.com>

A problem exists where a 32-bit application can have a huge bss, one that
is so large that an overflow of the TASK_SIZE happens.  But in this case,
the overflow is not detected in load_elf_binary().  Instead, because
arithmetic is being done using 32-bit containers, a truncation occurs and
the program gets loaded when it shouldn't have been.  Subsequent execution
yields unpredictable results.

The attached patch fixes this problem by checking for the overflow
condition and sending a SIGKILL to the application if the overflow is
detected.  This problem can in theory exist when loading the elf
interpreter as well, so a similar check was added there.


---

 25-akpm/fs/binfmt_elf.c |   25 +++++++++++++++++++++++++
 1 files changed, 25 insertions(+)

diff -puN fs/binfmt_elf.c~load_elf_binary-overflow-detection-fix fs/binfmt_elf.c
--- 25/fs/binfmt_elf.c~load_elf_binary-overflow-detection-fix	Thu Mar 25 14:24:33 2004
+++ 25-akpm/fs/binfmt_elf.c	Thu Mar 25 14:24:33 2004
@@ -360,6 +360,18 @@ static unsigned long load_elf_interp(str
 	    }
 
 	    /*
+	     * Check to see if the section's size will overflow the
+	     * allowed task size. Note that p_filesz must always be
+	     * <= p_memsize so it is only necessary to check p_memsz.
+	     */
+	    k = load_addr + eppnt->p_vaddr;
+	    if (k > TASK_SIZE || eppnt->p_filesz > eppnt->p_memsz ||
+		eppnt->p_memsz > TASK_SIZE || TASK_SIZE - eppnt->p_memsz < k) {
+	        error = -ENOMEM;
+		goto out_close;
+	    }
+
+	    /*
 	     * Find the end of the file mapping for this phdr, and keep
 	     * track of the largest address we see for this.
 	     */
@@ -759,6 +771,19 @@ static int load_elf_binary(struct linux_
 		if (k < start_code) start_code = k;
 		if (start_data < k) start_data = k;
 
+		/*
+		 * Check to see if the section's size will overflow the
+		 * allowed task size. Note that p_filesz must always be
+		 * <= p_memsz so it is only necessary to check p_memsz.
+		 */
+		if (k > TASK_SIZE || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
+		    elf_ppnt->p_memsz > TASK_SIZE ||
+		    TASK_SIZE - elf_ppnt->p_memsz < k) {
+			/* set_brk can never work.  Avoid overflows.  */
+			send_sig(SIGKILL, current, 0);
+			goto out_free_dentry;
+		}
+
 		k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
 
 		if (k > elf_bss)

_
