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

Merge branch 'exec_rm_compat' of git://git.kernel.org/pub/scm/linux/kernel/git/oleg/misc

* 'exec_rm_compat' of git://git.kernel.org/pub/scm/linux/kernel/git/oleg/misc:
exec: document acct_arg_size()
exec: unify do_execve/compat_do_execve code
exec: introduce struct user_arg_ptr
exec: introduce get_user_arg_ptr() helper

+100 -264
-235
fs/compat.c
··· 1306 1306 return do_sys_open(dfd, filename, flags, mode); 1307 1307 } 1308 1308 1309 - /* 1310 - * compat_count() counts the number of arguments/envelopes. It is basically 1311 - * a copy of count() from fs/exec.c, except that it works with 32 bit argv 1312 - * and envp pointers. 1313 - */ 1314 - static int compat_count(compat_uptr_t __user *argv, int max) 1315 - { 1316 - int i = 0; 1317 - 1318 - if (argv != NULL) { 1319 - for (;;) { 1320 - compat_uptr_t p; 1321 - 1322 - if (get_user(p, argv)) 1323 - return -EFAULT; 1324 - if (!p) 1325 - break; 1326 - argv++; 1327 - if (i++ >= max) 1328 - return -E2BIG; 1329 - 1330 - if (fatal_signal_pending(current)) 1331 - return -ERESTARTNOHAND; 1332 - cond_resched(); 1333 - } 1334 - } 1335 - return i; 1336 - } 1337 - 1338 - /* 1339 - * compat_copy_strings() is basically a copy of copy_strings() from fs/exec.c 1340 - * except that it works with 32 bit argv and envp pointers. 1341 - */ 1342 - static int compat_copy_strings(int argc, compat_uptr_t __user *argv, 1343 - struct linux_binprm *bprm) 1344 - { 1345 - struct page *kmapped_page = NULL; 1346 - char *kaddr = NULL; 1347 - unsigned long kpos = 0; 1348 - int ret; 1349 - 1350 - while (argc-- > 0) { 1351 - compat_uptr_t str; 1352 - int len; 1353 - unsigned long pos; 1354 - 1355 - if (get_user(str, argv+argc) || 1356 - !(len = strnlen_user(compat_ptr(str), MAX_ARG_STRLEN))) { 1357 - ret = -EFAULT; 1358 - goto out; 1359 - } 1360 - 1361 - if (len > MAX_ARG_STRLEN) { 1362 - ret = -E2BIG; 1363 - goto out; 1364 - } 1365 - 1366 - /* We're going to work our way backwords. */ 1367 - pos = bprm->p; 1368 - str += len; 1369 - bprm->p -= len; 1370 - 1371 - while (len > 0) { 1372 - int offset, bytes_to_copy; 1373 - 1374 - if (fatal_signal_pending(current)) { 1375 - ret = -ERESTARTNOHAND; 1376 - goto out; 1377 - } 1378 - cond_resched(); 1379 - 1380 - offset = pos % PAGE_SIZE; 1381 - if (offset == 0) 1382 - offset = PAGE_SIZE; 1383 - 1384 - bytes_to_copy = offset; 1385 - if (bytes_to_copy > len) 1386 - bytes_to_copy = len; 1387 - 1388 - offset -= bytes_to_copy; 1389 - pos -= bytes_to_copy; 1390 - str -= bytes_to_copy; 1391 - len -= bytes_to_copy; 1392 - 1393 - if (!kmapped_page || kpos != (pos & PAGE_MASK)) { 1394 - struct page *page; 1395 - 1396 - page = get_arg_page(bprm, pos, 1); 1397 - if (!page) { 1398 - ret = -E2BIG; 1399 - goto out; 1400 - } 1401 - 1402 - if (kmapped_page) { 1403 - flush_kernel_dcache_page(kmapped_page); 1404 - kunmap(kmapped_page); 1405 - put_page(kmapped_page); 1406 - } 1407 - kmapped_page = page; 1408 - kaddr = kmap(kmapped_page); 1409 - kpos = pos & PAGE_MASK; 1410 - flush_cache_page(bprm->vma, kpos, 1411 - page_to_pfn(kmapped_page)); 1412 - } 1413 - if (copy_from_user(kaddr+offset, compat_ptr(str), 1414 - bytes_to_copy)) { 1415 - ret = -EFAULT; 1416 - goto out; 1417 - } 1418 - } 1419 - } 1420 - ret = 0; 1421 - out: 1422 - if (kmapped_page) { 1423 - flush_kernel_dcache_page(kmapped_page); 1424 - kunmap(kmapped_page); 1425 - put_page(kmapped_page); 1426 - } 1427 - return ret; 1428 - } 1429 - 1430 - /* 1431 - * compat_do_execve() is mostly a copy of do_execve(), with the exception 1432 - * that it processes 32 bit argv and envp pointers. 1433 - */ 1434 - int compat_do_execve(char * filename, 1435 - compat_uptr_t __user *argv, 1436 - compat_uptr_t __user *envp, 1437 - struct pt_regs * regs) 1438 - { 1439 - struct linux_binprm *bprm; 1440 - struct file *file; 1441 - struct files_struct *displaced; 1442 - bool clear_in_exec; 1443 - int retval; 1444 - 1445 - retval = unshare_files(&displaced); 1446 - if (retval) 1447 - goto out_ret; 1448 - 1449 - retval = -ENOMEM; 1450 - bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); 1451 - if (!bprm) 1452 - goto out_files; 1453 - 1454 - retval = prepare_bprm_creds(bprm); 1455 - if (retval) 1456 - goto out_free; 1457 - 1458 - retval = check_unsafe_exec(bprm); 1459 - if (retval < 0) 1460 - goto out_free; 1461 - clear_in_exec = retval; 1462 - current->in_execve = 1; 1463 - 1464 - file = open_exec(filename); 1465 - retval = PTR_ERR(file); 1466 - if (IS_ERR(file)) 1467 - goto out_unmark; 1468 - 1469 - sched_exec(); 1470 - 1471 - bprm->file = file; 1472 - bprm->filename = filename; 1473 - bprm->interp = filename; 1474 - 1475 - retval = bprm_mm_init(bprm); 1476 - if (retval) 1477 - goto out_file; 1478 - 1479 - bprm->argc = compat_count(argv, MAX_ARG_STRINGS); 1480 - if ((retval = bprm->argc) < 0) 1481 - goto out; 1482 - 1483 - bprm->envc = compat_count(envp, MAX_ARG_STRINGS); 1484 - if ((retval = bprm->envc) < 0) 1485 - goto out; 1486 - 1487 - retval = prepare_binprm(bprm); 1488 - if (retval < 0) 1489 - goto out; 1490 - 1491 - retval = copy_strings_kernel(1, &bprm->filename, bprm); 1492 - if (retval < 0) 1493 - goto out; 1494 - 1495 - bprm->exec = bprm->p; 1496 - retval = compat_copy_strings(bprm->envc, envp, bprm); 1497 - if (retval < 0) 1498 - goto out; 1499 - 1500 - retval = compat_copy_strings(bprm->argc, argv, bprm); 1501 - if (retval < 0) 1502 - goto out; 1503 - 1504 - retval = search_binary_handler(bprm, regs); 1505 - if (retval < 0) 1506 - goto out; 1507 - 1508 - /* execve succeeded */ 1509 - current->fs->in_exec = 0; 1510 - current->in_execve = 0; 1511 - acct_update_integrals(current); 1512 - free_bprm(bprm); 1513 - if (displaced) 1514 - put_files_struct(displaced); 1515 - return retval; 1516 - 1517 - out: 1518 - if (bprm->mm) { 1519 - acct_arg_size(bprm, 0); 1520 - mmput(bprm->mm); 1521 - } 1522 - 1523 - out_file: 1524 - if (bprm->file) { 1525 - allow_write_access(bprm->file); 1526 - fput(bprm->file); 1527 - } 1528 - 1529 - out_unmark: 1530 - if (clear_in_exec) 1531 - current->fs->in_exec = 0; 1532 - current->in_execve = 0; 1533 - 1534 - out_free: 1535 - free_bprm(bprm); 1536 - 1537 - out_files: 1538 - if (displaced) 1539 - reset_files_struct(displaced); 1540 - out_ret: 1541 - return retval; 1542 - } 1543 - 1544 1309 #define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t)) 1545 1310 1546 1311 static int poll_select_copy_remaining(struct timespec *end_time, void __user *p,
+100 -25
fs/exec.c
··· 55 55 #include <linux/fs_struct.h> 56 56 #include <linux/pipe_fs_i.h> 57 57 #include <linux/oom.h> 58 + #include <linux/compat.h> 58 59 59 60 #include <asm/uaccess.h> 60 61 #include <asm/mmu_context.h> ··· 167 166 } 168 167 169 168 #ifdef CONFIG_MMU 170 - 171 - void acct_arg_size(struct linux_binprm *bprm, unsigned long pages) 169 + /* 170 + * The nascent bprm->mm is not visible until exec_mmap() but it can 171 + * use a lot of memory, account these pages in current->mm temporary 172 + * for oom_badness()->get_mm_rss(). Once exec succeeds or fails, we 173 + * change the counter back via acct_arg_size(0). 174 + */ 175 + static void acct_arg_size(struct linux_binprm *bprm, unsigned long pages) 172 176 { 173 177 struct mm_struct *mm = current->mm; 174 178 long diff = (long)(pages - bprm->vma_pages); ··· 192 186 #endif 193 187 } 194 188 195 - struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, 189 + static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, 196 190 int write) 197 191 { 198 192 struct page *page; ··· 311 305 312 306 #else 313 307 314 - void acct_arg_size(struct linux_binprm *bprm, unsigned long pages) 308 + static inline void acct_arg_size(struct linux_binprm *bprm, unsigned long pages) 315 309 { 316 310 } 317 311 318 - struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, 312 + static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, 319 313 int write) 320 314 { 321 315 struct page *page; ··· 404 398 return err; 405 399 } 406 400 401 + struct user_arg_ptr { 402 + #ifdef CONFIG_COMPAT 403 + bool is_compat; 404 + #endif 405 + union { 406 + const char __user *const __user *native; 407 + #ifdef CONFIG_COMPAT 408 + compat_uptr_t __user *compat; 409 + #endif 410 + } ptr; 411 + }; 412 + 413 + static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr) 414 + { 415 + const char __user *native; 416 + 417 + #ifdef CONFIG_COMPAT 418 + if (unlikely(argv.is_compat)) { 419 + compat_uptr_t compat; 420 + 421 + if (get_user(compat, argv.ptr.compat + nr)) 422 + return ERR_PTR(-EFAULT); 423 + 424 + return compat_ptr(compat); 425 + } 426 + #endif 427 + 428 + if (get_user(native, argv.ptr.native + nr)) 429 + return ERR_PTR(-EFAULT); 430 + 431 + return native; 432 + } 433 + 407 434 /* 408 435 * count() counts the number of strings in array ARGV. 409 436 */ 410 - static int count(const char __user * const __user * argv, int max) 437 + static int count(struct user_arg_ptr argv, int max) 411 438 { 412 439 int i = 0; 413 440 414 - if (argv != NULL) { 441 + if (argv.ptr.native != NULL) { 415 442 for (;;) { 416 - const char __user * p; 443 + const char __user *p = get_user_arg_ptr(argv, i); 417 444 418 - if (get_user(p, argv)) 419 - return -EFAULT; 420 445 if (!p) 421 446 break; 422 - argv++; 447 + 448 + if (IS_ERR(p)) 449 + return -EFAULT; 450 + 423 451 if (i++ >= max) 424 452 return -E2BIG; 425 453 ··· 470 430 * processes's memory to the new process's stack. The call to get_user_pages() 471 431 * ensures the destination page is created and not swapped out. 472 432 */ 473 - static int copy_strings(int argc, const char __user *const __user *argv, 433 + static int copy_strings(int argc, struct user_arg_ptr argv, 474 434 struct linux_binprm *bprm) 475 435 { 476 436 struct page *kmapped_page = NULL; ··· 483 443 int len; 484 444 unsigned long pos; 485 445 486 - if (get_user(str, argv+argc) || 487 - !(len = strnlen_user(str, MAX_ARG_STRLEN))) { 488 - ret = -EFAULT; 446 + ret = -EFAULT; 447 + str = get_user_arg_ptr(argv, argc); 448 + if (IS_ERR(str)) 489 449 goto out; 490 - } 491 450 492 - if (!valid_arg_len(bprm, len)) { 493 - ret = -E2BIG; 451 + len = strnlen_user(str, MAX_ARG_STRLEN); 452 + if (!len) 494 453 goto out; 495 - } 454 + 455 + ret = -E2BIG; 456 + if (!valid_arg_len(bprm, len)) 457 + goto out; 496 458 497 459 /* We're going to work our way backwords. */ 498 460 pos = bprm->p; ··· 561 519 /* 562 520 * Like copy_strings, but get argv and its values from kernel memory. 563 521 */ 564 - int copy_strings_kernel(int argc, const char *const *argv, 522 + int copy_strings_kernel(int argc, const char *const *__argv, 565 523 struct linux_binprm *bprm) 566 524 { 567 525 int r; 568 526 mm_segment_t oldfs = get_fs(); 527 + struct user_arg_ptr argv = { 528 + .ptr.native = (const char __user *const __user *)__argv, 529 + }; 530 + 569 531 set_fs(KERNEL_DS); 570 - r = copy_strings(argc, (const char __user *const __user *)argv, bprm); 532 + r = copy_strings(argc, argv, bprm); 571 533 set_fs(oldfs); 534 + 572 535 return r; 573 536 } 574 537 EXPORT_SYMBOL(copy_strings_kernel); ··· 1426 1379 /* 1427 1380 * sys_execve() executes a new program. 1428 1381 */ 1429 - int do_execve(const char * filename, 1430 - const char __user *const __user *argv, 1431 - const char __user *const __user *envp, 1432 - struct pt_regs * regs) 1382 + static int do_execve_common(const char *filename, 1383 + struct user_arg_ptr argv, 1384 + struct user_arg_ptr envp, 1385 + struct pt_regs *regs) 1433 1386 { 1434 1387 struct linux_binprm *bprm; 1435 1388 struct file *file; ··· 1535 1488 out_ret: 1536 1489 return retval; 1537 1490 } 1491 + 1492 + int do_execve(const char *filename, 1493 + const char __user *const __user *__argv, 1494 + const char __user *const __user *__envp, 1495 + struct pt_regs *regs) 1496 + { 1497 + struct user_arg_ptr argv = { .ptr.native = __argv }; 1498 + struct user_arg_ptr envp = { .ptr.native = __envp }; 1499 + return do_execve_common(filename, argv, envp, regs); 1500 + } 1501 + 1502 + #ifdef CONFIG_COMPAT 1503 + int compat_do_execve(char *filename, 1504 + compat_uptr_t __user *__argv, 1505 + compat_uptr_t __user *__envp, 1506 + struct pt_regs *regs) 1507 + { 1508 + struct user_arg_ptr argv = { 1509 + .is_compat = true, 1510 + .ptr.compat = __argv, 1511 + }; 1512 + struct user_arg_ptr envp = { 1513 + .is_compat = true, 1514 + .ptr.compat = __envp, 1515 + }; 1516 + return do_execve_common(filename, argv, envp, regs); 1517 + } 1518 + #endif 1538 1519 1539 1520 void set_binfmt(struct linux_binfmt *new) 1540 1521 {
-4
include/linux/binfmts.h
··· 60 60 unsigned long loader, exec; 61 61 }; 62 62 63 - extern void acct_arg_size(struct linux_binprm *bprm, unsigned long pages); 64 - extern struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, 65 - int write); 66 - 67 63 #define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0 68 64 #define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT) 69 65