Skip to contentSkip to conversation

Pass correct length to strnlen_user in fs/exec.c

PATCH In review Ryan Mallon 3 participants 5 messages Sep 8, 2011 – Sep 17, 2011

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Pass correct length to strnlen_user in fs/exec.c

Mail client was still mis-behaving. Should be fixed now.

---
Replace valid_arg_len function in fs/exec.c with max_arg_len function and pass the correct length to strnlen_user.

---

fs/exec.c +8 -7
index e19588c..12d2f7f 100644
@@ -296,9 +296,9 @@ err:
 	return err;
 }
 
-static bool valid_arg_len(struct linux_binprm *bprm, long len)
+static long max_arg_len(struct linux_binprm *bprm)
 {
-	return len <= MAX_ARG_STRLEN;
+	return MAX_ARG_STRLEN;
 }
 
 #else
@@ -354,9 +354,9 @@ static int __bprm_mm_init(struct linux_binprm *bprm)
 	return 0;
 }
 
-static bool valid_arg_len(struct linux_binprm *bprm, long len)
+static long max_arg_len(struct linux_binprm *bprm)
 {
-	return len <= bprm->p;
+	return bprm->p;
 }
 
 #endif /* CONFIG_MMU */
@@ -474,18 +474,19 @@ static int copy_strings(int argc, struct user_arg_ptr argv,
 		const char __user *str;
 		int len;
 		unsigned long pos;
+		long max_len = max_arg_len(bprm);
 
 		ret = -EFAULT;
 		str = get_user_arg_ptr(argv, argc);
 		if (IS_ERR(str))
 			goto out;
 
-		len = strnlen_user(str, MAX_ARG_STRLEN);
-		if (!len || len > MAX_ARG_STRLEN)
+		len = strnlen_user(str, max_len);
+		if (!len)
 			goto out;
 
 		ret = -E2BIG;
-		if (!valid_arg_len(bprm, len))
+		if (len > max_len)
 			goto out;
 
 		/* We're going to work our way backwords. */
#1 lore raw
Quoted 42 lines · On Thu, 08 Sep 2011 10:39:24 +1000 Ryan Mallon <[email protected]> wrote:

Replace valid_arg_len function in fs/exec.c with max_arg_len function and pass the correct length to strnlen_user.

fs/exec.c +5 -6
@@ -296,9 +296,9 @@ err:
 	return err;
 }
 
-static bool valid_arg_len(struct linux_binprm *bprm, long len)
+static long max_arg_len(struct linux_binprm *bprm)
 {
-	return len <= MAX_ARG_STRLEN;
+	return MAX_ARG_STRLEN;
 }
 
 #else
@@ -354,9 +354,9 @@ static int __bprm_mm_init(struct linux_binprm *bprm)
 	return 0;
 }
 
-static bool valid_arg_len(struct linux_binprm *bprm, long len)
+static long max_arg_len(struct linux_binprm *bprm)
 {
-	return len <= bprm->p;
+	return bprm->p;
 }
 
 #endif /* CONFIG_MMU */
@@ -474,18 +474,19 @@ static int copy_strings(int argc, struct user_arg_ptr argv,
 		const char __user *str;
 		int len;
 		unsigned long pos;
+		long max_len = max_arg_len(bprm);
 
 		ret = -EFAULT;
 		str = get_user_arg_ptr(argv, argc);
 		if (IS_ERR(str))
 			goto out;
 
-		len = strnlen_user(str, MAX_ARG_STRLEN);
-		if (!len || len > MAX_ARG_STRLEN)

fs/exec.c static int copy_strings(int argc, struct user_arg_ptr argv, · line 492

Quoted 10 lines
fs/exec.c +3 -1
+		len = strnlen_user(str, max_len);
+		if (!len)
 			goto out;
 
 		ret = -E2BIG;
-		if (!valid_arg_len(bprm, len))
+		if (len > max_len)
 			goto out;
 
 		/* We're going to work our way backwords. */

I'm struggling to find a reason to merge this - it churns code around rather pointlessly?

#2 lore raw
Quoted 49 lines · Andrew Morton
Quoted 46 lines · On Thu, 08 Sep 2011 10:39:24 +1000 Ryan Mallon <[email protected]> wrote:

Replace valid_arg_len function in fs/exec.c with max_arg_len function and pass the correct length to strnlen_user.

fs/exec.c +7 -6
@@ -296,9 +296,9 @@ err:
 	return err;
 }
 
-static bool valid_arg_len(struct linux_binprm *bprm, long len)
+static long max_arg_len(struct linux_binprm *bprm)
 {
-	return len <= MAX_ARG_STRLEN;
+	return MAX_ARG_STRLEN;
 }
 
 #else
@@ -354,9 +354,9 @@ static int __bprm_mm_init(struct linux_binprm *bprm)
 	return 0;
 }
 
-static bool valid_arg_len(struct linux_binprm *bprm, long len)
+static long max_arg_len(struct linux_binprm *bprm)
 {
-	return len <= bprm->p;
+	return bprm->p;
 }
 
 #endif /* CONFIG_MMU */
@@ -474,18 +474,19 @@ static int copy_strings(int argc, struct user_arg_ptr argv,
 		const char __user *str;
 		int len;
 		unsigned long pos;
+		long max_len = max_arg_len(bprm);
 
 		ret = -EFAULT;
 		str = get_user_arg_ptr(argv, argc);
 		if (IS_ERR(str))
 			goto out;
 
-		len = strnlen_user(str, MAX_ARG_STRLEN);
-		if (!len || len > MAX_ARG_STRLEN)
+		len = strnlen_user(str, max_len);
+		if (!len)
 			goto out;
 
Quoted 9 lines

fs/exec.c static int copy_strings(int argc, struct user_arg_ptr argv, · line 492

Quoted 6 lines
fs/exec.c +1 -1
 		ret = -E2BIG;
-		if (!valid_arg_len(bprm, len))
+		if (len > max_len)
 			goto out;
 
 		/* We're going to work our way backwords. */

I'm struggling to find a reason to merge this - it churns code around rather pointlessly?

That's fine. I originally went looking after a discussion with Mark about the weird strnlen_user semantics and this usage looked incorrect to me because it wasn't obviously checking >= MAX_ARG_STRLEN.

The rework I think makes it a bit more clear and passes the correct max length to strnlen_user. Its a bit odd to pass MAX_ARG_STRLEN and then check if it is longer than bprm->len, and I guess assumes that bprm->len is less than MAX_ARG_STRLEN.

Feel free to drop the patch if you think it is just churn.

~Ryan

#3 lore raw

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 6 lines · Ryan Mallon
Quoted 1 line

...

That's fine. I originally went looking after a discussion with Mark about the weird strnlen_user semantics and this usage looked incorrect to me because it wasn't obviously checking >= MAX_ARG_STRLEN.

Quoted 6 lines

The rework I think makes it a bit more clear and passes the correct max length to strnlen_user. Its a bit odd to pass MAX_ARG_STRLEN and then check if it is longer than bprm->len, and I guess assumes that bprm->len is less than MAX_ARG_STRLEN.

Feel free to drop the patch if you think it is just churn.

As you are already busy with fs/exec.c and strnlen_user, could you please check, if the timerace (argv/envp corruption) using strnlen_user could also be fixed within the same patch (if bug still in kernel). See

https://bugzilla.kernel.org/show_bug.cgi?id=39222

The race occurs when one thread issues exec while another one with shared memory pages is flipping char/0-bytes of the args or envptrs, causing random split (number greater maxargs - no problem, silently droped)/fusion (greater MAX_ARG_STRLEN - might harm dumb programs) or removal of all args including program name on top of new stack when calling via binfmt handlers (might have influenced old ldlinux/libc pre 2009?? when handling suid binaries)

Kind Regards,
Roman

- --
http://www.halfdog.net/
PGP: 156A AE98 B91F 0114 FE88 2BD8 C459 9386 feed a bee -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQFOc3tJxFmThv7tq+4RAibwAJ9CmDTlODxFuNR4KrSh9PGjgyXxQwCfeIgo ARy24fJ6DxuKmcPNla6f0lE=
=h1TA
-----END PGP SIGNATURE-----

#4 lore raw
Quoted 16 lines · halfdog

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Quoted 11 lines · Ryan Mallon
Quoted 1 line

...

That's fine. I originally went looking after a discussion with Mark about the weird strnlen_user semantics and this usage looked incorrect to me because it wasn't obviously checking >= MAX_ARG_STRLEN.

The rework I think makes it a bit more clear and passes the correct max length to strnlen_user. Its a bit odd to pass MAX_ARG_STRLEN and then check if it is longer than bprm->len, and I guess assumes that bprm->len is less than MAX_ARG_STRLEN.

Quoted 7 lines
Quoted 1 line

Feel free to drop the patch if you think it is just churn.

As you are already busy with fs/exec.c and strnlen_user, could you please check, if the timerace (argv/envp corruption) using strnlen_user could also be fixed within the same patch (if bug still in kernel). See

https://bugzilla.kernel.org/show_bug.cgi?id=39222

With kernel.org currently being down I cannot see that bug report unfortunately, so I don't know what the bug you are referring to is.

Quoted 7 lines

The race occurs when one thread issues exec while another one with shared memory pages is flipping char/0-bytes of the args or envptrs, causing random split (number greater maxargs - no problem, silently droped)/fusion (greater MAX_ARG_STRLEN - might harm dumb programs) or removal of all args including program name on top of new stack when calling via binfmt handlers (might have influenced old ldlinux/libc pre 2009?? when handling suid binaries)

It doesn't sound like my patch would have fixed that. The only issue that my patch would have corrected is the case where bprm->len is greater than MAX_ARG_STRLEN. MAX_ARG_STRLEN is pretty big though (32 * PAGE_SIZE), so I don't see that being an issue very often.

~Ryan