diff options
author | Xiaotian Feng <dfeng@redhat.com> | 2010-10-27 18:34:08 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-27 21:03:12 -0400 |
commit | 1b0d300bd0f047e2edaf9d4b6784189e6c67c3d1 (patch) | |
tree | ce3777527c949add56658e4d5b2ab02781590931 /fs | |
parent | 9b1bf12d5d51bca178dea21b04a0805e29d60cf1 (diff) |
core_pattern: fix truncation by core_pattern handler with long parameters
We met a parameter truncated issue, consider following:
> echo "|/root/core_pattern_pipe_test %p /usr/libexec/blah-blah-blah \
%s %c %p %u %g 11 12345678901234567890123456789012345678 %t" > \
/proc/sys/kernel/core_pattern
This is okay because the strings is less than CORENAME_MAX_SIZE. "cat
/proc/sys/kernel/core_pattern" shows the whole string. but after we run
core_pattern_pipe_test in man page, we found last parameter was truncated
like below:
argc[10]=<12807486>
The root cause is core_pattern allows % specifiers, which need to be
replaced during parse time, but the replace may expand the strings to
larger than CORENAME_MAX_SIZE. So if the last parameter is % specifiers,
the replace code is using snprintf(out_ptr, out_end - out_ptr, ...), this
will write out of corename array.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Xiaotian Feng <dfeng@redhat.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Reviewed-by: Neil Horman <nhorman@tuxdriver.com>
Cc: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/exec.c | 155 |
1 files changed, 95 insertions, 60 deletions
@@ -66,6 +66,12 @@ char core_pattern[CORENAME_MAX_SIZE] = "core"; | |||
66 | unsigned int core_pipe_limit; | 66 | unsigned int core_pipe_limit; |
67 | int suid_dumpable = 0; | 67 | int suid_dumpable = 0; |
68 | 68 | ||
69 | struct core_name { | ||
70 | char *corename; | ||
71 | int used, size; | ||
72 | }; | ||
73 | static atomic_t call_count = ATOMIC_INIT(1); | ||
74 | |||
69 | /* The maximal length of core_pattern is also specified in sysctl.c */ | 75 | /* The maximal length of core_pattern is also specified in sysctl.c */ |
70 | 76 | ||
71 | static LIST_HEAD(formats); | 77 | static LIST_HEAD(formats); |
@@ -1459,127 +1465,148 @@ void set_binfmt(struct linux_binfmt *new) | |||
1459 | 1465 | ||
1460 | EXPORT_SYMBOL(set_binfmt); | 1466 | EXPORT_SYMBOL(set_binfmt); |
1461 | 1467 | ||
1468 | static int expand_corename(struct core_name *cn) | ||
1469 | { | ||
1470 | char *old_corename = cn->corename; | ||
1471 | |||
1472 | cn->size = CORENAME_MAX_SIZE * atomic_inc_return(&call_count); | ||
1473 | cn->corename = krealloc(old_corename, cn->size, GFP_KERNEL); | ||
1474 | |||
1475 | if (!cn->corename) { | ||
1476 | kfree(old_corename); | ||
1477 | return -ENOMEM; | ||
1478 | } | ||
1479 | |||
1480 | return 0; | ||
1481 | } | ||
1482 | |||
1483 | static int cn_printf(struct core_name *cn, const char *fmt, ...) | ||
1484 | { | ||
1485 | char *cur; | ||
1486 | int need; | ||
1487 | int ret; | ||
1488 | va_list arg; | ||
1489 | |||
1490 | va_start(arg, fmt); | ||
1491 | need = vsnprintf(NULL, 0, fmt, arg); | ||
1492 | va_end(arg); | ||
1493 | |||
1494 | if (likely(need < cn->size - cn->used - 1)) | ||
1495 | goto out_printf; | ||
1496 | |||
1497 | ret = expand_corename(cn); | ||
1498 | if (ret) | ||
1499 | goto expand_fail; | ||
1500 | |||
1501 | out_printf: | ||
1502 | cur = cn->corename + cn->used; | ||
1503 | va_start(arg, fmt); | ||
1504 | vsnprintf(cur, need + 1, fmt, arg); | ||
1505 | va_end(arg); | ||
1506 | cn->used += need; | ||
1507 | return 0; | ||
1508 | |||
1509 | expand_fail: | ||
1510 | return ret; | ||
1511 | } | ||
1512 | |||
1462 | /* format_corename will inspect the pattern parameter, and output a | 1513 | /* format_corename will inspect the pattern parameter, and output a |
1463 | * name into corename, which must have space for at least | 1514 | * name into corename, which must have space for at least |
1464 | * CORENAME_MAX_SIZE bytes plus one byte for the zero terminator. | 1515 | * CORENAME_MAX_SIZE bytes plus one byte for the zero terminator. |
1465 | */ | 1516 | */ |
1466 | static int format_corename(char *corename, long signr) | 1517 | static int format_corename(struct core_name *cn, long signr) |
1467 | { | 1518 | { |
1468 | const struct cred *cred = current_cred(); | 1519 | const struct cred *cred = current_cred(); |
1469 | const char *pat_ptr = core_pattern; | 1520 | const char *pat_ptr = core_pattern; |
1470 | int ispipe = (*pat_ptr == '|'); | 1521 | int ispipe = (*pat_ptr == '|'); |
1471 | char *out_ptr = corename; | ||
1472 | char *const out_end = corename + CORENAME_MAX_SIZE; | ||
1473 | int rc; | ||
1474 | int pid_in_pattern = 0; | 1522 | int pid_in_pattern = 0; |
1523 | int err = 0; | ||
1524 | |||
1525 | cn->size = CORENAME_MAX_SIZE * atomic_read(&call_count); | ||
1526 | cn->corename = kmalloc(cn->size, GFP_KERNEL); | ||
1527 | cn->used = 0; | ||
1528 | |||
1529 | if (!cn->corename) | ||
1530 | return -ENOMEM; | ||
1475 | 1531 | ||
1476 | /* Repeat as long as we have more pattern to process and more output | 1532 | /* Repeat as long as we have more pattern to process and more output |
1477 | space */ | 1533 | space */ |
1478 | while (*pat_ptr) { | 1534 | while (*pat_ptr) { |
1479 | if (*pat_ptr != '%') { | 1535 | if (*pat_ptr != '%') { |
1480 | if (out_ptr == out_end) | 1536 | if (*pat_ptr == 0) |
1481 | goto out; | 1537 | goto out; |
1482 | *out_ptr++ = *pat_ptr++; | 1538 | err = cn_printf(cn, "%c", *pat_ptr++); |
1483 | } else { | 1539 | } else { |
1484 | switch (*++pat_ptr) { | 1540 | switch (*++pat_ptr) { |
1541 | /* single % at the end, drop that */ | ||
1485 | case 0: | 1542 | case 0: |
1486 | goto out; | 1543 | goto out; |
1487 | /* Double percent, output one percent */ | 1544 | /* Double percent, output one percent */ |
1488 | case '%': | 1545 | case '%': |
1489 | if (out_ptr == out_end) | 1546 | err = cn_printf(cn, "%c", '%'); |
1490 | goto out; | ||
1491 | *out_ptr++ = '%'; | ||
1492 | break; | 1547 | break; |
1493 | /* pid */ | 1548 | /* pid */ |
1494 | case 'p': | 1549 | case 'p': |
1495 | pid_in_pattern = 1; | 1550 | pid_in_pattern = 1; |
1496 | rc = snprintf(out_ptr, out_end - out_ptr, | 1551 | err = cn_printf(cn, "%d", |
1497 | "%d", task_tgid_vnr(current)); | 1552 | task_tgid_vnr(current)); |
1498 | if (rc > out_end - out_ptr) | ||
1499 | goto out; | ||
1500 | out_ptr += rc; | ||
1501 | break; | 1553 | break; |
1502 | /* uid */ | 1554 | /* uid */ |
1503 | case 'u': | 1555 | case 'u': |
1504 | rc = snprintf(out_ptr, out_end - out_ptr, | 1556 | err = cn_printf(cn, "%d", cred->uid); |
1505 | "%d", cred->uid); | ||
1506 | if (rc > out_end - out_ptr) | ||
1507 | goto out; | ||
1508 | out_ptr += rc; | ||
1509 | break; | 1557 | break; |
1510 | /* gid */ | 1558 | /* gid */ |
1511 | case 'g': | 1559 | case 'g': |
1512 | rc = snprintf(out_ptr, out_end - out_ptr, | 1560 | err = cn_printf(cn, "%d", cred->gid); |
1513 | "%d", cred->gid); | ||
1514 | if (rc > out_end - out_ptr) | ||
1515 | goto out; | ||
1516 | out_ptr += rc; | ||
1517 | break; | 1561 | break; |
1518 | /* signal that caused the coredump */ | 1562 | /* signal that caused the coredump */ |
1519 | case 's': | 1563 | case 's': |
1520 | rc = snprintf(out_ptr, out_end - out_ptr, | 1564 | err = cn_printf(cn, "%ld", signr); |
1521 | "%ld", signr); | ||
1522 | if (rc > out_end - out_ptr) | ||
1523 | goto out; | ||
1524 | out_ptr += rc; | ||
1525 | break; | 1565 | break; |
1526 | /* UNIX time of coredump */ | 1566 | /* UNIX time of coredump */ |
1527 | case 't': { | 1567 | case 't': { |
1528 | struct timeval tv; | 1568 | struct timeval tv; |
1529 | do_gettimeofday(&tv); | 1569 | do_gettimeofday(&tv); |
1530 | rc = snprintf(out_ptr, out_end - out_ptr, | 1570 | err = cn_printf(cn, "%lu", tv.tv_sec); |
1531 | "%lu", tv.tv_sec); | ||
1532 | if (rc > out_end - out_ptr) | ||
1533 | goto out; | ||
1534 | out_ptr += rc; | ||
1535 | break; | 1571 | break; |
1536 | } | 1572 | } |
1537 | /* hostname */ | 1573 | /* hostname */ |
1538 | case 'h': | 1574 | case 'h': |
1539 | down_read(&uts_sem); | 1575 | down_read(&uts_sem); |
1540 | rc = snprintf(out_ptr, out_end - out_ptr, | 1576 | err = cn_printf(cn, "%s", |
1541 | "%s", utsname()->nodename); | 1577 | utsname()->nodename); |
1542 | up_read(&uts_sem); | 1578 | up_read(&uts_sem); |
1543 | if (rc > out_end - out_ptr) | ||
1544 | goto out; | ||
1545 | out_ptr += rc; | ||
1546 | break; | 1579 | break; |
1547 | /* executable */ | 1580 | /* executable */ |
1548 | case 'e': | 1581 | case 'e': |
1549 | rc = snprintf(out_ptr, out_end - out_ptr, | 1582 | err = cn_printf(cn, "%s", current->comm); |
1550 | "%s", current->comm); | ||
1551 | if (rc > out_end - out_ptr) | ||
1552 | goto out; | ||
1553 | out_ptr += rc; | ||
1554 | break; | 1583 | break; |
1555 | /* core limit size */ | 1584 | /* core limit size */ |
1556 | case 'c': | 1585 | case 'c': |
1557 | rc = snprintf(out_ptr, out_end - out_ptr, | 1586 | err = cn_printf(cn, "%lu", |
1558 | "%lu", rlimit(RLIMIT_CORE)); | 1587 | rlimit(RLIMIT_CORE)); |
1559 | if (rc > out_end - out_ptr) | ||
1560 | goto out; | ||
1561 | out_ptr += rc; | ||
1562 | break; | 1588 | break; |
1563 | default: | 1589 | default: |
1564 | break; | 1590 | break; |
1565 | } | 1591 | } |
1566 | ++pat_ptr; | 1592 | ++pat_ptr; |
1567 | } | 1593 | } |
1594 | |||
1595 | if (err) | ||
1596 | return err; | ||
1568 | } | 1597 | } |
1598 | |||
1569 | /* Backward compatibility with core_uses_pid: | 1599 | /* Backward compatibility with core_uses_pid: |
1570 | * | 1600 | * |
1571 | * If core_pattern does not include a %p (as is the default) | 1601 | * If core_pattern does not include a %p (as is the default) |
1572 | * and core_uses_pid is set, then .%pid will be appended to | 1602 | * and core_uses_pid is set, then .%pid will be appended to |
1573 | * the filename. Do not do this for piped commands. */ | 1603 | * the filename. Do not do this for piped commands. */ |
1574 | if (!ispipe && !pid_in_pattern && core_uses_pid) { | 1604 | if (!ispipe && !pid_in_pattern && core_uses_pid) { |
1575 | rc = snprintf(out_ptr, out_end - out_ptr, | 1605 | err = cn_printf(cn, ".%d", task_tgid_vnr(current)); |
1576 | ".%d", task_tgid_vnr(current)); | 1606 | if (err) |
1577 | if (rc > out_end - out_ptr) | 1607 | return err; |
1578 | goto out; | ||
1579 | out_ptr += rc; | ||
1580 | } | 1608 | } |
1581 | out: | 1609 | out: |
1582 | *out_ptr = 0; | ||
1583 | return ispipe; | 1610 | return ispipe; |
1584 | } | 1611 | } |
1585 | 1612 | ||
@@ -1856,7 +1883,7 @@ static int umh_pipe_setup(struct subprocess_info *info) | |||
1856 | void do_coredump(long signr, int exit_code, struct pt_regs *regs) | 1883 | void do_coredump(long signr, int exit_code, struct pt_regs *regs) |
1857 | { | 1884 | { |
1858 | struct core_state core_state; | 1885 | struct core_state core_state; |
1859 | char corename[CORENAME_MAX_SIZE + 1]; | 1886 | struct core_name cn; |
1860 | struct mm_struct *mm = current->mm; | 1887 | struct mm_struct *mm = current->mm; |
1861 | struct linux_binfmt * binfmt; | 1888 | struct linux_binfmt * binfmt; |
1862 | const struct cred *old_cred; | 1889 | const struct cred *old_cred; |
@@ -1911,7 +1938,13 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) | |||
1911 | */ | 1938 | */ |
1912 | clear_thread_flag(TIF_SIGPENDING); | 1939 | clear_thread_flag(TIF_SIGPENDING); |
1913 | 1940 | ||
1914 | ispipe = format_corename(corename, signr); | 1941 | ispipe = format_corename(&cn, signr); |
1942 | |||
1943 | if (ispipe == -ENOMEM) { | ||
1944 | printk(KERN_WARNING "format_corename failed\n"); | ||
1945 | printk(KERN_WARNING "Aborting core\n"); | ||
1946 | goto fail_corename; | ||
1947 | } | ||
1915 | 1948 | ||
1916 | if (ispipe) { | 1949 | if (ispipe) { |
1917 | int dump_count; | 1950 | int dump_count; |
@@ -1948,7 +1981,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) | |||
1948 | goto fail_dropcount; | 1981 | goto fail_dropcount; |
1949 | } | 1982 | } |
1950 | 1983 | ||
1951 | helper_argv = argv_split(GFP_KERNEL, corename+1, NULL); | 1984 | helper_argv = argv_split(GFP_KERNEL, cn.corename+1, NULL); |
1952 | if (!helper_argv) { | 1985 | if (!helper_argv) { |
1953 | printk(KERN_WARNING "%s failed to allocate memory\n", | 1986 | printk(KERN_WARNING "%s failed to allocate memory\n", |
1954 | __func__); | 1987 | __func__); |
@@ -1961,7 +1994,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) | |||
1961 | argv_free(helper_argv); | 1994 | argv_free(helper_argv); |
1962 | if (retval) { | 1995 | if (retval) { |
1963 | printk(KERN_INFO "Core dump to %s pipe failed\n", | 1996 | printk(KERN_INFO "Core dump to %s pipe failed\n", |
1964 | corename); | 1997 | cn.corename); |
1965 | goto close_fail; | 1998 | goto close_fail; |
1966 | } | 1999 | } |
1967 | } else { | 2000 | } else { |
@@ -1970,7 +2003,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) | |||
1970 | if (cprm.limit < binfmt->min_coredump) | 2003 | if (cprm.limit < binfmt->min_coredump) |
1971 | goto fail_unlock; | 2004 | goto fail_unlock; |
1972 | 2005 | ||
1973 | cprm.file = filp_open(corename, | 2006 | cprm.file = filp_open(cn.corename, |
1974 | O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, | 2007 | O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, |
1975 | 0600); | 2008 | 0600); |
1976 | if (IS_ERR(cprm.file)) | 2009 | if (IS_ERR(cprm.file)) |
@@ -2012,6 +2045,8 @@ fail_dropcount: | |||
2012 | if (ispipe) | 2045 | if (ispipe) |
2013 | atomic_dec(&core_dump_count); | 2046 | atomic_dec(&core_dump_count); |
2014 | fail_unlock: | 2047 | fail_unlock: |
2048 | kfree(cn.corename); | ||
2049 | fail_corename: | ||
2015 | coredump_finish(mm); | 2050 | coredump_finish(mm); |
2016 | revert_creds(old_cred); | 2051 | revert_creds(old_cred); |
2017 | fail_creds: | 2052 | fail_creds: |