aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2011-03-06 12:02:54 -0500
committerOleg Nesterov <oleg@redhat.com>2011-04-09 09:53:56 -0400
commit0e028465d18b7c6797fcbdea632299d16097c5cd (patch)
treed03a1f0f688e9c4a780b2a1a3ef8354378b0ad42
parentba2d01629d0d167598cfea85adc7926822bbfc45 (diff)
exec: unify do_execve/compat_do_execve code
Add the appropriate members into struct user_arg_ptr and teach get_user_arg_ptr() to handle is_compat = T case correctly. This allows us to remove the compat_do_execve() code from fs/compat.c and reimplement compat_do_execve() as the trivial wrapper on top of do_execve_common(is_compat => true). In fact, this fixes another (minor) bug. "compat_uptr_t str" can overflow after "str += len" in compat_copy_strings() if a 64bit application execs via sys32_execve(). Unexport acct_arg_size() and get_arg_page(), fs/compat.c doesn't need them any longer. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Reviewed-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com> Tested-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
-rw-r--r--fs/compat.c235
-rw-r--r--fs/exec.c62
-rw-r--r--include/linux/binfmts.h4
3 files changed, 50 insertions, 251 deletions
diff --git a/fs/compat.c b/fs/compat.c
index 72fe6cda9108..0ea00832de23 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1306,241 +1306,6 @@ compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, int
1306 return do_sys_open(dfd, filename, flags, mode); 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 */
1314static 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 */
1342static 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;
1421out:
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 */
1434int 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
1517out:
1518 if (bprm->mm) {
1519 acct_arg_size(bprm, 0);
1520 mmput(bprm->mm);
1521 }
1522
1523out_file:
1524 if (bprm->file) {
1525 allow_write_access(bprm->file);
1526 fput(bprm->file);
1527 }
1528
1529out_unmark:
1530 if (clear_in_exec)
1531 current->fs->in_exec = 0;
1532 current->in_execve = 0;
1533
1534out_free:
1535 free_bprm(bprm);
1536
1537out_files:
1538 if (displaced)
1539 reset_files_struct(displaced);
1540out_ret:
1541 return retval;
1542}
1543
1544#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t)) 1309#define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
1545 1310
1546static int poll_select_copy_remaining(struct timespec *end_time, void __user *p, 1311static int poll_select_copy_remaining(struct timespec *end_time, void __user *p,
diff --git a/fs/exec.c b/fs/exec.c
index 526a0399d963..89d788ca7829 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -55,6 +55,7 @@
55#include <linux/fs_struct.h> 55#include <linux/fs_struct.h>
56#include <linux/pipe_fs_i.h> 56#include <linux/pipe_fs_i.h>
57#include <linux/oom.h> 57#include <linux/oom.h>
58#include <linux/compat.h>
58 59
59#include <asm/uaccess.h> 60#include <asm/uaccess.h>
60#include <asm/mmu_context.h> 61#include <asm/mmu_context.h>
@@ -167,7 +168,7 @@ out:
167 168
168#ifdef CONFIG_MMU 169#ifdef CONFIG_MMU
169 170
170void acct_arg_size(struct linux_binprm *bprm, unsigned long pages) 171static void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
171{ 172{
172 struct mm_struct *mm = current->mm; 173 struct mm_struct *mm = current->mm;
173 long diff = (long)(pages - bprm->vma_pages); 174 long diff = (long)(pages - bprm->vma_pages);
@@ -186,7 +187,7 @@ void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
186#endif 187#endif
187} 188}
188 189
189struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, 190static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
190 int write) 191 int write)
191{ 192{
192 struct page *page; 193 struct page *page;
@@ -305,11 +306,11 @@ static bool valid_arg_len(struct linux_binprm *bprm, long len)
305 306
306#else 307#else
307 308
308void acct_arg_size(struct linux_binprm *bprm, unsigned long pages) 309static inline void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
309{ 310{
310} 311}
311 312
312struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, 313static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
313 int write) 314 int write)
314{ 315{
315 struct page *page; 316 struct page *page;
@@ -399,17 +400,36 @@ err:
399} 400}
400 401
401struct user_arg_ptr { 402struct user_arg_ptr {
402 const char __user *const __user *native; 403#ifdef CONFIG_COMPAT
404 bool is_compat;
405#endif
406 union {
407 const char __user *const __user *native;
408#ifdef CONFIG_COMPAT
409 compat_uptr_t __user *compat;
410#endif
411 } ptr;
403}; 412};
404 413
405static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr) 414static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
406{ 415{
407 const char __user *ptr; 416 const char __user *native;
417
418#ifdef CONFIG_COMPAT
419 if (unlikely(argv.is_compat)) {
420 compat_uptr_t compat;
421
422 if (get_user(compat, argv.ptr.compat + nr))
423 return ERR_PTR(-EFAULT);
408 424
409 if (get_user(ptr, argv.native + nr)) 425 return compat_ptr(compat);
426 }
427#endif
428
429 if (get_user(native, argv.ptr.native + nr))
410 return ERR_PTR(-EFAULT); 430 return ERR_PTR(-EFAULT);
411 431
412 return ptr; 432 return native;
413} 433}
414 434
415/* 435/*
@@ -419,7 +439,7 @@ static int count(struct user_arg_ptr argv, int max)
419{ 439{
420 int i = 0; 440 int i = 0;
421 441
422 if (argv.native != NULL) { 442 if (argv.ptr.native != NULL) {
423 for (;;) { 443 for (;;) {
424 const char __user *p = get_user_arg_ptr(argv, i); 444 const char __user *p = get_user_arg_ptr(argv, i);
425 445
@@ -542,7 +562,7 @@ int copy_strings_kernel(int argc, const char *const *__argv,
542 int r; 562 int r;
543 mm_segment_t oldfs = get_fs(); 563 mm_segment_t oldfs = get_fs();
544 struct user_arg_ptr argv = { 564 struct user_arg_ptr argv = {
545 .native = (const char __user *const __user *)__argv, 565 .ptr.native = (const char __user *const __user *)__argv,
546 }; 566 };
547 567
548 set_fs(KERNEL_DS); 568 set_fs(KERNEL_DS);
@@ -1516,10 +1536,28 @@ int do_execve(const char *filename,
1516 const char __user *const __user *__envp, 1536 const char __user *const __user *__envp,
1517 struct pt_regs *regs) 1537 struct pt_regs *regs)
1518{ 1538{
1519 struct user_arg_ptr argv = { .native = __argv }; 1539 struct user_arg_ptr argv = { .ptr.native = __argv };
1520 struct user_arg_ptr envp = { .native = __envp }; 1540 struct user_arg_ptr envp = { .ptr.native = __envp };
1541 return do_execve_common(filename, argv, envp, regs);
1542}
1543
1544#ifdef CONFIG_COMPAT
1545int compat_do_execve(char *filename,
1546 compat_uptr_t __user *__argv,
1547 compat_uptr_t __user *__envp,
1548 struct pt_regs *regs)
1549{
1550 struct user_arg_ptr argv = {
1551 .is_compat = true,
1552 .ptr.compat = __argv,
1553 };
1554 struct user_arg_ptr envp = {
1555 .is_compat = true,
1556 .ptr.compat = __envp,
1557 };
1521 return do_execve_common(filename, argv, envp, regs); 1558 return do_execve_common(filename, argv, envp, regs);
1522} 1559}
1560#endif
1523 1561
1524void set_binfmt(struct linux_binfmt *new) 1562void set_binfmt(struct linux_binfmt *new)
1525{ 1563{
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index c3d6512eded1..8845613fd7e3 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -60,10 +60,6 @@ struct linux_binprm {
60 unsigned long loader, exec; 60 unsigned long loader, exec;
61}; 61};
62 62
63extern void acct_arg_size(struct linux_binprm *bprm, unsigned long pages);
64extern struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
65 int write);
66
67#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0 63#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
68#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT) 64#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT)
69 65