aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-06-19 20:47:20 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-06-20 02:38:28 -0400
commitf5b65348fd77839b50e79bc0a5e536832ea52d8d (patch)
tree2b7843426934e646643fff30cb9608ce574e6cc6
parentba4dbdedd3edc2798659bcd8b1a184ea8bdd04dc (diff)
proc: fix missing final NUL in get_mm_cmdline() rewrite
The rewrite of the cmdline fetching missed the fact that we used to also return the final terminating NUL character of the last argument. I hadn't noticed, and none of the tools I tested cared, but something obviously must care, because Michal Kubecek noticed the change in behavior. Tweak the "find the end" logic to actually include the NUL character, and once past the eend of argv, always start the strnlen() at the expected (original) argument end. This whole "allow people to rewrite their arguments in place" is a nasty hack and requires that odd slop handling at the end of the argv array, but it's our traditional model, so we continue to support it. Repored-and-bisected-by: Michal Kubecek <mkubecek@suse.cz> Reviewed-and-tested-by: Michal Kubecek <mkubecek@suse.cz> Cc: Alexey Dobriyan <adobriyan@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/proc/base.c28
1 files changed, 23 insertions, 5 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index b6572944efc3..aaffc0c30216 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -235,6 +235,10 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
235 if (env_start != arg_end || env_start >= env_end) 235 if (env_start != arg_end || env_start >= env_end)
236 env_start = env_end = arg_end; 236 env_start = env_end = arg_end;
237 237
238 /* .. and limit it to a maximum of one page of slop */
239 if (env_end >= arg_end + PAGE_SIZE)
240 env_end = arg_end + PAGE_SIZE - 1;
241
238 /* We're not going to care if "*ppos" has high bits set */ 242 /* We're not going to care if "*ppos" has high bits set */
239 pos = arg_start + *ppos; 243 pos = arg_start + *ppos;
240 244
@@ -254,10 +258,19 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
254 while (count) { 258 while (count) {
255 int got; 259 int got;
256 size_t size = min_t(size_t, PAGE_SIZE, count); 260 size_t size = min_t(size_t, PAGE_SIZE, count);
261 long offset;
257 262
258 got = access_remote_vm(mm, pos, page, size, FOLL_ANON); 263 /*
259 if (got <= 0) 264 * Are we already starting past the official end?
265 * We always include the last byte that is *supposed*
266 * to be NUL
267 */
268 offset = (pos >= arg_end) ? pos - arg_end + 1 : 0;
269
270 got = access_remote_vm(mm, pos - offset, page, size + offset, FOLL_ANON);
271 if (got <= offset)
260 break; 272 break;
273 got -= offset;
261 274
262 /* Don't walk past a NUL character once you hit arg_end */ 275 /* Don't walk past a NUL character once you hit arg_end */
263 if (pos + got >= arg_end) { 276 if (pos + got >= arg_end) {
@@ -276,12 +289,17 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
276 n = arg_end - pos - 1; 289 n = arg_end - pos - 1;
277 290
278 /* Cut off at first NUL after 'n' */ 291 /* Cut off at first NUL after 'n' */
279 got = n + strnlen(page+n, got-n); 292 got = n + strnlen(page+n, offset+got-n);
280 if (!got) 293 if (got < offset)
281 break; 294 break;
295 got -= offset;
296
297 /* Include the NUL if it existed */
298 if (got < size)
299 got++;
282 } 300 }
283 301
284 got -= copy_to_user(buf, page, got); 302 got -= copy_to_user(buf, page+offset, got);
285 if (unlikely(!got)) { 303 if (unlikely(!got)) {
286 if (!len) 304 if (!len)
287 len = -EFAULT; 305 len = -EFAULT;