[PATCH] Fix buffer overflow in x86-64/ia64 32bit execve

Linux Kernel Mailing List <[email protected]> Thu, 30 Jun 2005 22:45:00 -0700
Newsgroups gmane.linux.kernel.commits.2-4
Message-ID <[email protected]>
tree 29e6ef82f987734d97da57af63a5f0410c21996c
parent bb6c40830e2f66b33c22275829a730ed078e430a
author Andi Kleen <[email protected]> Tue, 28 Jun 2005 15:16:52 +0200
committer Marcelo Tosatti <[email protected]> Wed, 29 Jun 2005 16:49:46 -0300

[PATCH] Fix buffer overflow in x86-64/ia64 32bit execve

Fix buffer overflow in x86-64/ia64 32bit execve

Originally noted by Ilja van Sprundel

I fixed it for both x86-64 and IA64. Other architectures
are not affected.

Signed-off-by: Andi Kleen <[email protected]>

 arch/ia64/ia32/sys_ia32.c   |   13 ++++++++-----
 arch/x86_64/ia32/sys_ia32.c |   17 +++++++++--------
 2 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c
--- a/arch/ia64/ia32/sys_ia32.c
+++ b/arch/ia64/ia32/sys_ia32.c
@@ -94,7 +94,7 @@ asmlinkage unsigned long sys_brk(unsigne
 static DECLARE_MUTEX(ia32_mmap_sem);
 
 static int
-nargs (unsigned int arg, char **ap)
+nargs (unsigned int arg, char **ap, int max)
 {
 	unsigned int addr;
 	int n, err;
@@ -107,6 +107,8 @@ nargs (unsigned int arg, char **ap)
 		err = get_user(addr, (unsigned int *)A(arg));
 		if (err)
 			return err;
+		if (n > max)
+			return -E2BIG;
 		if (ap)
 			*ap++ = (char *) A(addr);
 		arg += sizeof(unsigned int);
@@ -128,10 +130,11 @@ sys32_execve (char *filename, unsigned i
 	int na, ne, len;
 	long r;
 
-	na = nargs(argv, NULL);
+	/* Allocates upto 2x MAX_ARG_PAGES */
+	na = nargs(argv, NULL, (MAX_ARG_PAGES*PAGE_SIZE) / sizeof(char *) - 1);
 	if (na < 0)
 		return na;
-	ne = nargs(envp, NULL);
+	ne = nargs(envp, NULL, (MAX_ARG_PAGES*PAGE_SIZE) / sizeof(char *) - 1 );
 	if (ne < 0)
 		return ne;
 	len = (na + ne + 2) * sizeof(*av);
@@ -143,10 +146,10 @@ sys32_execve (char *filename, unsigned i
 	av[na] = NULL;
 	ae[ne] = NULL;
 
-	r = nargs(argv, av);
+	r = nargs(argv, av, na);
 	if (r < 0)
 		goto out;
-	r = nargs(envp, ae);
+	r = nargs(envp, ae, ne);
 	if (r < 0)
 		goto out;
 
diff --git a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c
--- a/arch/x86_64/ia32/sys_ia32.c
+++ b/arch/x86_64/ia32/sys_ia32.c
@@ -2200,7 +2200,7 @@ asmlinkage long sys32_ustat(dev_t dev, s
 	return ret;
 } 
 
-static int nargs(u32 src, char **dst) 
+static int nargs(u32 src, char **dst, int max) 
 { 
 	int cnt;
 	u32 val; 
@@ -2210,13 +2210,13 @@ static int nargs(u32 src, char **dst) 
 		int ret = get_user(val, (__u32 *)(u64)src); 
 		if (ret)
 			return ret;
+		if (cnt > max)
+			return -E2BIG; 
 		if (dst)
 			dst[cnt] = (char *)(u64)val; 
 		cnt++;
 		src += 4;
-		if (cnt >= (MAX_ARG_PAGES * PAGE_SIZE) / sizeof(char *))
-			return -E2BIG; 
-	} while(val); 
+		} while(val); 
 	if (dst)
 		dst[cnt-1] = 0; 
 	return cnt; 
@@ -2230,13 +2230,14 @@ asmlinkage long sys32_execve(char *name,
 	int ret;
 	unsigned sz = 0; 
 	
+	/* Can actually allocate 2*MAX_ARG_PAGES */
 	if (argv) {
-	na = nargs(argv, NULL); 
+	na = nargs(argv, NULL, (MAX_ARG_PAGES * PAGE_SIZE)/sizeof(char*) - 1); 
 	if (na < 0) 
 		return -EFAULT; 
 	} 	
 	if (envp) { 
-	ne = nargs(envp, NULL); 
+	ne = nargs(envp, NULL, (MAX_ARG_PAGES * PAGE_SIZE)/sizeof(char*) - 1); 
 	if (ne < 0) 
 		return -EFAULT; 
 	}
@@ -2252,13 +2253,13 @@ asmlinkage long sys32_execve(char *name,
 	} 
 	
 	if (argv) { 
-	ret = nargs(argv, buf);
+	ret = nargs(argv, buf, na);
 	if (ret < 0)
 		goto free;
 	}
 
 	if (envp) { 
-	ret = nargs(envp, buf + na); 
+	ret = nargs(envp, buf + na, ne); 
 	if (ret < 0)
 		goto free; 
 	}