Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'proc-cmdline' (/proc/<pid>/cmdline fixes)

This fixes two problems reported with the cmdline simplification and
cleanup last year:

- the setproctitle() special cases didn't quite match the original
semantics, and it can be noticeable:

https://lore.kernel.org/lkml/alpine.LNX.2.21.1904052326230.3249@kich.toxcorp.com/

- it could leak an uninitialized byte from the temporary buffer under
the right (wrong) circustances:

https://lore.kernel.org/lkml/20190712160913.17727-1-izbyshev@ispras.ru/

It rewrites the logic entirely, splitting it into two separate commits
(and two separate functions) for the two different cases ("unedited
cmdline" vs "setproctitle() has been used to change the command line").

* proc-cmdline:
/proc/<pid>/cmdline: add back the setproctitle() special case
/proc/<pid>/cmdline: remove all the special cases

+75 -57
+75 -57
fs/proc/base.c
··· 209 209 return result; 210 210 } 211 211 212 + /* 213 + * If the user used setproctitle(), we just get the string from 214 + * user space at arg_start, and limit it to a maximum of one page. 215 + */ 216 + static ssize_t get_mm_proctitle(struct mm_struct *mm, char __user *buf, 217 + size_t count, unsigned long pos, 218 + unsigned long arg_start) 219 + { 220 + char *page; 221 + int ret, got; 222 + 223 + if (pos >= PAGE_SIZE) 224 + return 0; 225 + 226 + page = (char *)__get_free_page(GFP_KERNEL); 227 + if (!page) 228 + return -ENOMEM; 229 + 230 + ret = 0; 231 + got = access_remote_vm(mm, arg_start, page, PAGE_SIZE, FOLL_ANON); 232 + if (got > 0) { 233 + int len = strnlen(page, got); 234 + 235 + /* Include the NUL character if it was found */ 236 + if (len < got) 237 + len++; 238 + 239 + if (len > pos) { 240 + len -= pos; 241 + if (len > count) 242 + len = count; 243 + len -= copy_to_user(buf, page+pos, len); 244 + if (!len) 245 + len = -EFAULT; 246 + ret = len; 247 + } 248 + } 249 + free_page((unsigned long)page); 250 + return ret; 251 + } 252 + 212 253 static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf, 213 254 size_t count, loff_t *ppos) 214 255 { 215 256 unsigned long arg_start, arg_end, env_start, env_end; 216 257 unsigned long pos, len; 217 - char *page; 258 + char *page, c; 218 259 219 260 /* Check if process spawned far enough to have cmdline. */ 220 261 if (!mm->env_end) ··· 272 231 return 0; 273 232 274 233 /* 275 - * We have traditionally allowed the user to re-write 276 - * the argument strings and overflow the end result 277 - * into the environment section. But only do that if 278 - * the environment area is contiguous to the arguments. 234 + * We allow setproctitle() to overwrite the argument 235 + * strings, and overflow past the original end. But 236 + * only when it overflows into the environment area. 279 237 */ 280 - if (env_start != arg_end || env_start >= env_end) 238 + if (env_start != arg_end || env_end < env_start) 281 239 env_start = env_end = arg_end; 282 - 283 - /* .. and limit it to a maximum of one page of slop */ 284 - if (env_end >= arg_end + PAGE_SIZE) 285 - env_end = arg_end + PAGE_SIZE - 1; 240 + len = env_end - arg_start; 286 241 287 242 /* We're not going to care if "*ppos" has high bits set */ 288 - pos = arg_start + *ppos; 289 - 290 - /* .. but we do check the result is in the proper range */ 291 - if (pos < arg_start || pos >= env_end) 243 + pos = *ppos; 244 + if (pos >= len) 245 + return 0; 246 + if (count > len - pos) 247 + count = len - pos; 248 + if (!count) 292 249 return 0; 293 250 294 - /* .. and we never go past env_end */ 295 - if (env_end - pos < count) 296 - count = env_end - pos; 251 + /* 252 + * Magical special case: if the argv[] end byte is not 253 + * zero, the user has overwritten it with setproctitle(3). 254 + * 255 + * Possible future enhancement: do this only once when 256 + * pos is 0, and set a flag in the 'struct file'. 257 + */ 258 + if (access_remote_vm(mm, arg_end-1, &c, 1, FOLL_ANON) == 1 && c) 259 + return get_mm_proctitle(mm, buf, count, pos, arg_start); 260 + 261 + /* 262 + * For the non-setproctitle() case we limit things strictly 263 + * to the [arg_start, arg_end[ range. 264 + */ 265 + pos += arg_start; 266 + if (pos < arg_start || pos >= arg_end) 267 + return 0; 268 + if (count > arg_end - pos) 269 + count = arg_end - pos; 297 270 298 271 page = (char *)__get_free_page(GFP_KERNEL); 299 272 if (!page) ··· 317 262 while (count) { 318 263 int got; 319 264 size_t size = min_t(size_t, PAGE_SIZE, count); 320 - long offset; 321 265 322 - /* 323 - * Are we already starting past the official end? 324 - * We always include the last byte that is *supposed* 325 - * to be NUL 326 - */ 327 - offset = (pos >= arg_end) ? pos - arg_end + 1 : 0; 328 - 329 - got = access_remote_vm(mm, pos - offset, page, size + offset, FOLL_ANON); 330 - if (got <= offset) 266 + got = access_remote_vm(mm, pos, page, size, FOLL_ANON); 267 + if (got <= 0) 331 268 break; 332 - got -= offset; 333 - 334 - /* Don't walk past a NUL character once you hit arg_end */ 335 - if (pos + got >= arg_end) { 336 - int n = 0; 337 - 338 - /* 339 - * If we started before 'arg_end' but ended up 340 - * at or after it, we start the NUL character 341 - * check at arg_end-1 (where we expect the normal 342 - * EOF to be). 343 - * 344 - * NOTE! This is smaller than 'got', because 345 - * pos + got >= arg_end 346 - */ 347 - if (pos < arg_end) 348 - n = arg_end - pos - 1; 349 - 350 - /* Cut off at first NUL after 'n' */ 351 - got = n + strnlen(page+n, offset+got-n); 352 - if (got < offset) 353 - break; 354 - got -= offset; 355 - 356 - /* Include the NUL if it existed */ 357 - if (got < size) 358 - got++; 359 - } 360 - 361 - got -= copy_to_user(buf, page+offset, got); 269 + got -= copy_to_user(buf, page, got); 362 270 if (unlikely(!got)) { 363 271 if (!len) 364 272 len = -EFAULT;