diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-10-09 16:29:44 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-10-09 16:29:44 +0000 |
| commit | afd7a8d744b29daaedbba1969307bd9ce17e7dc3 (patch) | |
| tree | 015fa25e84f72a32b31724add0969f51a03bb74d /shell | |
| parent | 07e05c100fe6ac2bc5aa3505daabb55224c06a1f (diff) | |
| download | busybox-w32-afd7a8d744b29daaedbba1969307bd9ce17e7dc3.tar.gz busybox-w32-afd7a8d744b29daaedbba1969307bd9ce17e7dc3.tar.bz2 busybox-w32-afd7a8d744b29daaedbba1969307bd9ce17e7dc3.zip | |
hush: fix environment and memory leaks, add tests for them
function old new delta
add_malloced_string_to_strings - 110 +110
run_list 1999 2086 +87
free_strings_and_unsetenv - 87 +87
hush_version_str - 18 +18
pseudo_exec_argv 139 146 +7
static.version_str 17 - -17
free_pipe 237 210 -27
done_word 790 642 -148
------------------------------------------------------------------------------
(add/remove: 3/1 grow/shrink: 2/2 up/down: 309/-192) Total: 117 bytes
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 149 | ||||
| -rw-r--r-- | shell/hush_test/hush-vars/var_leaks.right | 1 | ||||
| -rwxr-xr-x | shell/hush_test/hush-vars/var_leaks.tests | 14 | ||||
| -rwxr-xr-x | shell/hush_test/hush-z_slow/leak_var.tests | 47 | ||||
| -rw-r--r-- | shell/hush_test/hush-z_slow/leak_var2.right | 2 | ||||
| -rwxr-xr-x | shell/hush_test/hush-z_slow/leak_var2.tests | 63 |
6 files changed, 218 insertions, 58 deletions
diff --git a/shell/hush.c b/shell/hush.c index d5955dd93..7cf2f3322 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -54,7 +54,7 @@ | |||
| 54 | * port selected bugfixes from post-0.49 busybox lash - done? | 54 | * port selected bugfixes from post-0.49 busybox lash - done? |
| 55 | * change { and } from special chars to reserved words | 55 | * change { and } from special chars to reserved words |
| 56 | * builtins: return, trap, ulimit | 56 | * builtins: return, trap, ulimit |
| 57 | * test magic exec | 57 | * test magic exec with redirection only |
| 58 | * check setting of global_argc and global_argv | 58 | * check setting of global_argc and global_argv |
| 59 | * follow IFS rules more precisely, including update semantics | 59 | * follow IFS rules more precisely, including update semantics |
| 60 | * figure out what to do with backslash-newline | 60 | * figure out what to do with backslash-newline |
| @@ -73,7 +73,7 @@ | |||
| 73 | #include <fnmatch.h> | 73 | #include <fnmatch.h> |
| 74 | #endif | 74 | #endif |
| 75 | 75 | ||
| 76 | #define HUSH_VER_STR "0.9" | 76 | #define HUSH_VER_STR "0.91" |
| 77 | 77 | ||
| 78 | #if !BB_MMU && ENABLE_HUSH_TICK | 78 | #if !BB_MMU && ENABLE_HUSH_TICK |
| 79 | //#undef ENABLE_HUSH_TICK | 79 | //#undef ENABLE_HUSH_TICK |
| @@ -525,13 +525,13 @@ static int free_pipe(struct pipe *pi, int indent); | |||
| 525 | static int setup_redirects(struct command *prog, int squirrel[]); | 525 | static int setup_redirects(struct command *prog, int squirrel[]); |
| 526 | static int run_list(struct pipe *pi); | 526 | static int run_list(struct pipe *pi); |
| 527 | #if BB_MMU | 527 | #if BB_MMU |
| 528 | #define pseudo_exec_argv(ptrs2free, argv, assignment_cnt, argv_expanded) \ | 528 | #define pseudo_exec_argv(ptr_ptrs2free, argv, assignment_cnt, argv_expanded) \ |
| 529 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) | 529 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) |
| 530 | #define pseudo_exec(ptrs2free, command, argv_expanded) \ | 530 | #define pseudo_exec(ptr_ptrs2free, command, argv_expanded) \ |
| 531 | pseudo_exec(command, argv_expanded) | 531 | pseudo_exec(command, argv_expanded) |
| 532 | #endif | 532 | #endif |
| 533 | static void pseudo_exec_argv(char **ptrs2free, char **argv, int assignment_cnt, char **argv_expanded) NORETURN; | 533 | static void pseudo_exec_argv(char ***ptr_ptrs2free, char **argv, int assignment_cnt, char **argv_expanded) NORETURN; |
| 534 | static void pseudo_exec(char **ptrs2free, struct command *command, char **argv_expanded) NORETURN; | 534 | static void pseudo_exec(char ***ptr_ptrs2free, struct command *command, char **argv_expanded) NORETURN; |
| 535 | static int run_pipe(struct pipe *pi); | 535 | static int run_pipe(struct pipe *pi); |
| 536 | /* data structure manipulation: */ | 536 | /* data structure manipulation: */ |
| 537 | static int setup_redirect(struct parse_context *ctx, int fd, redir_type style, struct in_str *input); | 537 | static int setup_redirect(struct parse_context *ctx, int fd, redir_type style, struct in_str *input); |
| @@ -576,6 +576,9 @@ static int set_local_var(char *str, int flg_export); | |||
| 576 | static void unset_local_var(const char *name); | 576 | static void unset_local_var(const char *name); |
| 577 | 577 | ||
| 578 | 578 | ||
| 579 | static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="HUSH_VER_STR; | ||
| 580 | |||
| 581 | |||
| 579 | static int glob_needed(const char *s) | 582 | static int glob_needed(const char *s) |
| 580 | { | 583 | { |
| 581 | while (*s) { | 584 | while (*s) { |
| @@ -650,26 +653,44 @@ static char **add_malloced_string_to_strings(char **strings, char *add) | |||
| 650 | return add_malloced_strings_to_strings(strings, v); | 653 | return add_malloced_strings_to_strings(strings, v); |
| 651 | } | 654 | } |
| 652 | 655 | ||
| 653 | static void free_strings(char **strings) | 656 | static void free_strings_and_unsetenv(char **strings, int unset) |
| 654 | { | 657 | { |
| 655 | if (strings) { | 658 | char **v; |
| 656 | char **v = strings; | 659 | |
| 657 | while (*v) | 660 | if (!strings) |
| 658 | free(*v++); | 661 | return; |
| 659 | free(strings); | 662 | |
| 663 | v = strings; | ||
| 664 | while (*v) { | ||
| 665 | if (unset) { | ||
| 666 | char *copy; | ||
| 667 | #if !BB_MMU | ||
| 668 | /* If this is a magic guard pointer, do not free it, | ||
| 669 | * and stop unsetting */ | ||
| 670 | if (*v == hush_version_str) { | ||
| 671 | unset = 0; | ||
| 672 | v++; | ||
| 673 | continue; | ||
| 674 | } | ||
| 675 | #endif | ||
| 676 | /* *strchrnul(*v, '=') = '\0'; -- BAD | ||
| 677 | * In case *v was putenv'ed, we can't | ||
| 678 | * unsetenv(*v) after taking out '=': | ||
| 679 | * it won't work, env is modified by taking out! | ||
| 680 | * horror :( */ | ||
| 681 | copy = xstrndup(*v, strchrnul(*v, '=') - *v); | ||
| 682 | unsetenv(copy); | ||
| 683 | free(copy); | ||
| 684 | } | ||
| 685 | free(*v++); | ||
| 660 | } | 686 | } |
| 687 | free(strings); | ||
| 661 | } | 688 | } |
| 662 | 689 | ||
| 663 | #if !BB_MMU | 690 | static void free_strings(char **strings) |
| 664 | #define EXTRA_PTRS 5 /* 1 for NULL, 1 for args, 3 for paranoid reasons */ | ||
| 665 | static char **alloc_ptrs(char **argv) | ||
| 666 | { | 691 | { |
| 667 | char **v = argv; | 692 | free_strings_and_unsetenv(strings, 0); |
| 668 | while (*v) | ||
| 669 | v++; | ||
| 670 | return xzalloc((v - argv + EXTRA_PTRS) * sizeof(v[0])); | ||
| 671 | } | 693 | } |
| 672 | #endif | ||
| 673 | 694 | ||
| 674 | 695 | ||
| 675 | /* Function prototypes for builtins */ | 696 | /* Function prototypes for builtins */ |
| @@ -1393,34 +1414,36 @@ static void restore_redirects(int squirrel[]) | |||
| 1393 | * XXX no exit() here. If you don't exec, use _exit instead. | 1414 | * XXX no exit() here. If you don't exec, use _exit instead. |
| 1394 | * The at_exit handlers apparently confuse the calling process, | 1415 | * The at_exit handlers apparently confuse the calling process, |
| 1395 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ | 1416 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ |
| 1396 | static void pseudo_exec_argv(char **ptrs2free, char **argv, int assignment_cnt, char **argv_expanded) | 1417 | static void pseudo_exec_argv(char ***ptr_ptrs2free, char **argv, int assignment_cnt, char **argv_expanded) |
| 1397 | { | 1418 | { |
| 1398 | int i, rcode; | 1419 | int i, rcode; |
| 1399 | char *p; | 1420 | char *p; |
| 1400 | const struct built_in_command *x; | 1421 | const struct built_in_command *x; |
| 1401 | 1422 | ||
| 1402 | for (i = 0; i < assignment_cnt; i++) { | ||
| 1403 | debug_printf_exec("pid %d environment modification: %s\n", | ||
| 1404 | getpid(), argv[i]); | ||
| 1405 | p = expand_string_to_string(argv[i]); | ||
| 1406 | #if !BB_MMU | ||
| 1407 | *ptrs2free++ = p; | ||
| 1408 | #endif | ||
| 1409 | putenv(p); | ||
| 1410 | } | ||
| 1411 | argv += i; | ||
| 1412 | /* If a variable is assigned in a forest, and nobody listens, | 1423 | /* If a variable is assigned in a forest, and nobody listens, |
| 1413 | * was it ever really set? | 1424 | * was it ever really set? |
| 1414 | */ | 1425 | */ |
| 1415 | if (!argv[0]) | 1426 | if (!argv[assignment_cnt]) |
| 1416 | _exit(EXIT_SUCCESS); | 1427 | _exit(EXIT_SUCCESS); |
| 1417 | 1428 | ||
| 1429 | for (i = 0; i < assignment_cnt; i++) { | ||
| 1430 | debug_printf_exec("pid %d environment modification: %s\n", | ||
| 1431 | getpid(), *argv); | ||
| 1432 | p = expand_string_to_string(*argv); | ||
| 1433 | putenv(p); | ||
| 1434 | #if !BB_MMU | ||
| 1435 | *ptr_ptrs2free = add_malloced_string_to_strings(*ptr_ptrs2free, p); | ||
| 1436 | #endif | ||
| 1437 | argv++; | ||
| 1438 | } | ||
| 1418 | if (argv_expanded) { | 1439 | if (argv_expanded) { |
| 1419 | argv = argv_expanded; | 1440 | argv = argv_expanded; |
| 1420 | } else { | 1441 | } else { |
| 1421 | argv = expand_strvec_to_strvec(argv); | 1442 | argv = expand_strvec_to_strvec(argv); |
| 1422 | #if !BB_MMU | 1443 | #if !BB_MMU |
| 1423 | *ptrs2free++ = (char*) argv; | 1444 | /* Inserting magic guard pointer to not unsetenv junk later */ |
| 1445 | *ptr_ptrs2free = add_malloced_string_to_strings(*ptr_ptrs2free, (char*)hush_version_str); | ||
| 1446 | *ptr_ptrs2free = add_malloced_string_to_strings(*ptr_ptrs2free, (char*)argv); | ||
| 1424 | #endif | 1447 | #endif |
| 1425 | } | 1448 | } |
| 1426 | 1449 | ||
| @@ -1466,10 +1489,10 @@ static void pseudo_exec_argv(char **ptrs2free, char **argv, int assignment_cnt, | |||
| 1466 | 1489 | ||
| 1467 | /* Called after [v]fork() in run_pipe() | 1490 | /* Called after [v]fork() in run_pipe() |
| 1468 | */ | 1491 | */ |
| 1469 | static void pseudo_exec(char **ptrs2free, struct command *command, char **argv_expanded) | 1492 | static void pseudo_exec(char ***ptr_ptrs2free, struct command *command, char **argv_expanded) |
| 1470 | { | 1493 | { |
| 1471 | if (command->argv) | 1494 | if (command->argv) |
| 1472 | pseudo_exec_argv(ptrs2free, command->argv, command->assignment_cnt, argv_expanded); | 1495 | pseudo_exec_argv(ptr_ptrs2free, command->argv, command->assignment_cnt, argv_expanded); |
| 1473 | 1496 | ||
| 1474 | if (command->group) { | 1497 | if (command->group) { |
| 1475 | #if !BB_MMU | 1498 | #if !BB_MMU |
| @@ -1745,6 +1768,7 @@ static int run_pipe(struct pipe *pi) | |||
| 1745 | int nextin; | 1768 | int nextin; |
| 1746 | int pipefds[2]; /* pipefds[0] is for reading */ | 1769 | int pipefds[2]; /* pipefds[0] is for reading */ |
| 1747 | struct command *command; | 1770 | struct command *command; |
| 1771 | char **ptrs2free = NULL; | ||
| 1748 | char **argv_expanded = NULL; | 1772 | char **argv_expanded = NULL; |
| 1749 | char **argv; | 1773 | char **argv; |
| 1750 | const struct built_in_command *x; | 1774 | const struct built_in_command *x; |
| @@ -1796,7 +1820,7 @@ static int run_pipe(struct pipe *pi) | |||
| 1796 | for (i = 0; i < command->assignment_cnt; i++) { | 1820 | for (i = 0; i < command->assignment_cnt; i++) { |
| 1797 | p = expand_string_to_string(argv[i]); | 1821 | p = expand_string_to_string(argv[i]); |
| 1798 | putenv(p); | 1822 | putenv(p); |
| 1799 | //FIXME: do we leak p?! | 1823 | ptrs2free = add_malloced_string_to_strings(ptrs2free, p); |
| 1800 | } | 1824 | } |
| 1801 | 1825 | ||
| 1802 | /* Expand the rest into (possibly) many strings each */ | 1826 | /* Expand the rest into (possibly) many strings each */ |
| @@ -1805,9 +1829,10 @@ static int run_pipe(struct pipe *pi) | |||
| 1805 | for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { | 1829 | for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { |
| 1806 | if (strcmp(argv_expanded[0], x->cmd) == 0) { | 1830 | if (strcmp(argv_expanded[0], x->cmd) == 0) { |
| 1807 | if (x->function == builtin_exec && argv_expanded[1] == NULL) { | 1831 | if (x->function == builtin_exec && argv_expanded[1] == NULL) { |
| 1808 | debug_printf("magic exec\n"); | 1832 | debug_printf("exec with redirects only\n"); |
| 1809 | setup_redirects(command, NULL); | 1833 | setup_redirects(command, NULL); |
| 1810 | return EXIT_SUCCESS; | 1834 | rcode = EXIT_SUCCESS; |
| 1835 | goto clean_up_and_ret1; | ||
| 1811 | } | 1836 | } |
| 1812 | debug_printf("builtin inline %s\n", argv_expanded[0]); | 1837 | debug_printf("builtin inline %s\n", argv_expanded[0]); |
| 1813 | /* XXX setup_redirects acts on file descriptors, not FILEs. | 1838 | /* XXX setup_redirects acts on file descriptors, not FILEs. |
| @@ -1817,10 +1842,13 @@ static int run_pipe(struct pipe *pi) | |||
| 1817 | setup_redirects(command, squirrel); | 1842 | setup_redirects(command, squirrel); |
| 1818 | debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv_expanded[1]); | 1843 | debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv_expanded[1]); |
| 1819 | rcode = x->function(argv_expanded) & 0xff; | 1844 | rcode = x->function(argv_expanded) & 0xff; |
| 1820 | free(argv_expanded); | 1845 | USE_FEATURE_SH_STANDALONE(clean_up_and_ret:) |
| 1821 | restore_redirects(squirrel); | 1846 | restore_redirects(squirrel); |
| 1822 | debug_printf_exec("run_pipe return %d\n", rcode); | 1847 | clean_up_and_ret1: |
| 1848 | free_strings_and_unsetenv(ptrs2free, 1); | ||
| 1849 | free(argv_expanded); | ||
| 1823 | IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) | 1850 | IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) |
| 1851 | debug_printf_exec("run_pipe return %d\n", rcode); | ||
| 1824 | return rcode; | 1852 | return rcode; |
| 1825 | } | 1853 | } |
| 1826 | } | 1854 | } |
| @@ -1832,11 +1860,7 @@ static int run_pipe(struct pipe *pi) | |||
| 1832 | save_nofork_data(&G.nofork_save); | 1860 | save_nofork_data(&G.nofork_save); |
| 1833 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); | 1861 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); |
| 1834 | rcode = run_nofork_applet_prime(&G.nofork_save, a, argv_expanded); | 1862 | rcode = run_nofork_applet_prime(&G.nofork_save, a, argv_expanded); |
| 1835 | free(argv_expanded); | 1863 | goto clean_up_and_ret; |
| 1836 | restore_redirects(squirrel); | ||
| 1837 | debug_printf_exec("run_pipe return %d\n", rcode); | ||
| 1838 | IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) | ||
| 1839 | return rcode; | ||
| 1840 | } | 1864 | } |
| 1841 | } | 1865 | } |
| 1842 | #endif | 1866 | #endif |
| @@ -1856,14 +1880,15 @@ static int run_pipe(struct pipe *pi) | |||
| 1856 | 1880 | ||
| 1857 | for (i = 0; i < pi->num_cmds; i++) { | 1881 | for (i = 0; i < pi->num_cmds; i++) { |
| 1858 | #if !BB_MMU | 1882 | #if !BB_MMU |
| 1859 | char **ptrs2free = NULL; | 1883 | /* Avoid confusion WHAT is volatile. Pointer is volatile, |
| 1884 | * not the stuff it points to. */ | ||
| 1885 | typedef char **ppchar_t; | ||
| 1886 | volatile ppchar_t shared_across_vfork; | ||
| 1860 | #endif | 1887 | #endif |
| 1888 | |||
| 1861 | command = &(pi->cmds[i]); | 1889 | command = &(pi->cmds[i]); |
| 1862 | if (command->argv) { | 1890 | if (command->argv) { |
| 1863 | debug_printf_exec(": pipe member '%s' '%s'...\n", command->argv[0], command->argv[1]); | 1891 | debug_printf_exec(": pipe member '%s' '%s'...\n", command->argv[0], command->argv[1]); |
| 1864 | #if !BB_MMU | ||
| 1865 | ptrs2free = alloc_ptrs(command->argv); | ||
| 1866 | #endif | ||
| 1867 | } else | 1892 | } else |
| 1868 | debug_printf_exec(": pipe member with no argv\n"); | 1893 | debug_printf_exec(": pipe member with no argv\n"); |
| 1869 | 1894 | ||
| @@ -1873,6 +1898,9 @@ static int run_pipe(struct pipe *pi) | |||
| 1873 | if ((i + 1) < pi->num_cmds) | 1898 | if ((i + 1) < pi->num_cmds) |
| 1874 | xpipe(pipefds); | 1899 | xpipe(pipefds); |
| 1875 | 1900 | ||
| 1901 | #if !BB_MMU | ||
| 1902 | shared_across_vfork = ptrs2free; | ||
| 1903 | #endif | ||
| 1876 | command->pid = BB_MMU ? fork() : vfork(); | 1904 | command->pid = BB_MMU ? fork() : vfork(); |
| 1877 | if (!command->pid) { /* child */ | 1905 | if (!command->pid) { /* child */ |
| 1878 | if (ENABLE_HUSH_JOB) | 1906 | if (ENABLE_HUSH_JOB) |
| @@ -1906,13 +1934,19 @@ static int run_pipe(struct pipe *pi) | |||
| 1906 | set_jobctrl_sighandler(SIG_DFL); | 1934 | set_jobctrl_sighandler(SIG_DFL); |
| 1907 | set_misc_sighandler(SIG_DFL); | 1935 | set_misc_sighandler(SIG_DFL); |
| 1908 | signal(SIGCHLD, SIG_DFL); | 1936 | signal(SIGCHLD, SIG_DFL); |
| 1909 | pseudo_exec(ptrs2free, command, argv_expanded); /* does not return */ | 1937 | /* comment how it sets env???? |
| 1938 | for single_and_fg, it's already set yes? */ | ||
| 1939 | pseudo_exec((char ***) &shared_across_vfork, command, argv_expanded); | ||
| 1940 | /* pseudo_exec() does not return */ | ||
| 1910 | } | 1941 | } |
| 1911 | free(argv_expanded); | 1942 | /* parent */ |
| 1912 | argv_expanded = NULL; | ||
| 1913 | #if !BB_MMU | 1943 | #if !BB_MMU |
| 1914 | free_strings(ptrs2free); | 1944 | ptrs2free = shared_across_vfork; |
| 1915 | #endif | 1945 | #endif |
| 1946 | free(argv_expanded); | ||
| 1947 | argv_expanded = NULL; | ||
| 1948 | free_strings_and_unsetenv(ptrs2free, 1); | ||
| 1949 | ptrs2free = NULL; | ||
| 1916 | if (command->pid < 0) { /* [v]fork failed */ | 1950 | if (command->pid < 0) { /* [v]fork failed */ |
| 1917 | /* Clearly indicate, was it fork or vfork */ | 1951 | /* Clearly indicate, was it fork or vfork */ |
| 1918 | bb_perror_msg(BB_MMU ? "fork" : "vfork"); | 1952 | bb_perror_msg(BB_MMU ? "fork" : "vfork"); |
| @@ -4077,10 +4111,9 @@ static void setup_job_control(void) | |||
| 4077 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 4111 | int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 4078 | int hush_main(int argc, char **argv) | 4112 | int hush_main(int argc, char **argv) |
| 4079 | { | 4113 | { |
| 4080 | static const char version_str[] ALIGN1 = "HUSH_VERSION="HUSH_VER_STR; | ||
| 4081 | static const struct variable const_shell_ver = { | 4114 | static const struct variable const_shell_ver = { |
| 4082 | .next = NULL, | 4115 | .next = NULL, |
| 4083 | .varstr = (char*)version_str, | 4116 | .varstr = (char*)hush_version_str, |
| 4084 | .max_len = 1, /* 0 can provoke free(name) */ | 4117 | .max_len = 1, /* 0 can provoke free(name) */ |
| 4085 | .flg_export = 1, | 4118 | .flg_export = 1, |
| 4086 | .flg_read_only = 1, | 4119 | .flg_read_only = 1, |
| @@ -4114,7 +4147,7 @@ int hush_main(int argc, char **argv) | |||
| 4114 | } | 4147 | } |
| 4115 | e++; | 4148 | e++; |
| 4116 | } | 4149 | } |
| 4117 | putenv((char *)version_str); /* reinstate HUSH_VERSION */ | 4150 | putenv((char *)hush_version_str); /* reinstate HUSH_VERSION */ |
| 4118 | 4151 | ||
| 4119 | #if ENABLE_FEATURE_EDITING | 4152 | #if ENABLE_FEATURE_EDITING |
| 4120 | G.line_input_state = new_line_input_t(FOR_SHELL); | 4153 | G.line_input_state = new_line_input_t(FOR_SHELL); |
| @@ -4338,10 +4371,10 @@ static int builtin_exec(char **argv) | |||
| 4338 | return EXIT_SUCCESS; /* bash does this */ | 4371 | return EXIT_SUCCESS; /* bash does this */ |
| 4339 | { | 4372 | { |
| 4340 | #if !BB_MMU | 4373 | #if !BB_MMU |
| 4341 | char **ptrs2free = alloc_ptrs(argv); | 4374 | char **ptrs2free = NULL; |
| 4342 | #endif | 4375 | #endif |
| 4343 | // FIXME: if exec fails, bash does NOT exit! We do... | 4376 | // FIXME: if exec fails, bash does NOT exit! We do... |
| 4344 | pseudo_exec_argv(ptrs2free, argv + 1, 0, NULL); | 4377 | pseudo_exec_argv(&ptrs2free, argv + 1, 0, NULL); |
| 4345 | /* never returns */ | 4378 | /* never returns */ |
| 4346 | } | 4379 | } |
| 4347 | } | 4380 | } |
diff --git a/shell/hush_test/hush-vars/var_leaks.right b/shell/hush_test/hush-vars/var_leaks.right new file mode 100644 index 000000000..d86bac9de --- /dev/null +++ b/shell/hush_test/hush-vars/var_leaks.right | |||
| @@ -0,0 +1 @@ | |||
| OK | |||
diff --git a/shell/hush_test/hush-vars/var_leaks.tests b/shell/hush_test/hush-vars/var_leaks.tests new file mode 100755 index 000000000..27c8c6504 --- /dev/null +++ b/shell/hush_test/hush-vars/var_leaks.tests | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # external program | ||
| 2 | a=b /bin/true | ||
| 3 | env | grep ^a= | ||
| 4 | |||
| 5 | # builtin | ||
| 6 | a=b true | ||
| 7 | env | grep ^a= | ||
| 8 | |||
| 9 | # exec with redirection only | ||
| 10 | # in bash, this leaks! | ||
| 11 | a=b exec 1>&1 | ||
| 12 | env | grep ^a= | ||
| 13 | |||
| 14 | echo OK | ||
diff --git a/shell/hush_test/hush-z_slow/leak_var.tests b/shell/hush_test/hush-z_slow/leak_var.tests index 388d6a734..b3e13e308 100755 --- a/shell/hush_test/hush-z_slow/leak_var.tests +++ b/shell/hush_test/hush-z_slow/leak_var.tests | |||
| @@ -42,6 +42,53 @@ while test $i != X; do | |||
| 42 | done | 42 | done |
| 43 | end=`ps -o pid,vsz | grep "^ *$pid "` | 43 | end=`ps -o pid,vsz | grep "^ *$pid "` |
| 44 | 44 | ||
| 45 | # Warm up again (I do need it on my machine) | ||
| 46 | beg=`ps -o pid,vsz | grep "^ *$pid "` | ||
| 47 | i=1 | ||
| 48 | while test $i != X; do | ||
| 49 | unset t | ||
| 50 | t=111111111111111111111111111111111111111111111111111111111111111111111111 | ||
| 51 | export t | ||
| 52 | unset t | ||
| 53 | t=111111111111111111111111111111111111111111111111111111111111111111111111 | ||
| 54 | export t | ||
| 55 | unset t | ||
| 56 | t=111111111111111111111111111111111111111111111111111111111111111111111111 | ||
| 57 | export t | ||
| 58 | unset t | ||
| 59 | t=111111111111111111111111111111111111111111111111111111111111111111111111 | ||
| 60 | export t | ||
| 61 | unset t | ||
| 62 | t=111111111111111111111111111111111111111111111111111111111111111111111111 | ||
| 63 | export t | ||
| 64 | i=1$i | ||
| 65 | if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi | ||
| 66 | if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi | ||
| 67 | if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi | ||
| 68 | if test $i = 1111111111111111111111111111111111111111111114; then i=5; fi | ||
| 69 | if test $i = 1111111111111111111111111111111111111111111115; then i=6; fi | ||
| 70 | if test $i = 1111111111111111111111111111111111111111111116; then i=7; fi | ||
| 71 | if test $i = 1111111111111111111111111111111111111111111117; then i=8; fi | ||
| 72 | if test $i = 1111111111111111111111111111111111111111111118; then i=9; fi | ||
| 73 | if test $i = 1111111111111111111111111111111111111111111119; then i=a; fi | ||
| 74 | if test $i = 111111111111111111111111111111111111111111111a; then i=b; fi | ||
| 75 | if test $i = 111111111111111111111111111111111111111111111b; then i=c; fi | ||
| 76 | if test $i = 111111111111111111111111111111111111111111111c; then i=d; fi | ||
| 77 | if test $i = 111111111111111111111111111111111111111111111d; then i=e; fi | ||
| 78 | if test $i = 111111111111111111111111111111111111111111111e; then i=f; fi | ||
| 79 | if test $i = 111111111111111111111111111111111111111111111f; then i=g; fi | ||
| 80 | if test $i = 111111111111111111111111111111111111111111111g; then i=h; fi | ||
| 81 | if test $i = 111111111111111111111111111111111111111111111h; then i=i; fi | ||
| 82 | if test $i = 111111111111111111111111111111111111111111111i; then i=j; fi | ||
| 83 | if test $i = 111111111111111111111111111111111111111111111j; then i=X; fi | ||
| 84 | done | ||
| 85 | end=`ps -o pid,vsz | grep "^ *$pid "` | ||
| 86 | if test "$beg" != "$end"; then | ||
| 87 | true echo "vsz grows: $beg -> $end" | ||
| 88 | else | ||
| 89 | true echo "vsz does not grow" | ||
| 90 | fi | ||
| 91 | |||
| 45 | echo "Measuring memory leak..." | 92 | echo "Measuring memory leak..." |
| 46 | beg=`ps -o pid,vsz | grep "^ *$pid "` | 93 | beg=`ps -o pid,vsz | grep "^ *$pid "` |
| 47 | i=1 | 94 | i=1 |
diff --git a/shell/hush_test/hush-z_slow/leak_var2.right b/shell/hush_test/hush-z_slow/leak_var2.right new file mode 100644 index 000000000..7bccc1eef --- /dev/null +++ b/shell/hush_test/hush-z_slow/leak_var2.right | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | Measuring memory leak... | ||
| 2 | vsz does not grow | ||
diff --git a/shell/hush_test/hush-z_slow/leak_var2.tests b/shell/hush_test/hush-z_slow/leak_var2.tests new file mode 100755 index 000000000..09f247552 --- /dev/null +++ b/shell/hush_test/hush-z_slow/leak_var2.tests | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | pid=$$ | ||
| 2 | |||
| 3 | t=1 | ||
| 4 | export t | ||
| 5 | |||
| 6 | # Warm up | ||
| 7 | beg=`ps -o pid,vsz | grep "^ *$pid "` | ||
| 8 | i=1 | ||
| 9 | while test $i != X; do | ||
| 10 | t=111111111111111111111111111111111111111111111111111111111111111111111110$i | ||
| 11 | t=111111111111111111111111111111111111111111111111111111111111111111111111$i true | ||
| 12 | t=111111111111111111111111111111111111111111111111111111111111111111111112$i /bin/true | ||
| 13 | t=111111111111111111111111111111111111111111111111111111111111111111111113$i exec 1>&1 | ||
| 14 | i=1$i | ||
| 15 | if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi | ||
| 16 | if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi | ||
| 17 | if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi | ||
| 18 | if test $i = 1111111111111111111111111111111111111111111114; then i=X; fi | ||
| 19 | done | ||
| 20 | end=`ps -o pid,vsz | grep "^ *$pid "` | ||
| 21 | |||
| 22 | # Warm up again (I do need it on my machine) | ||
| 23 | beg=`ps -o pid,vsz | grep "^ *$pid "` | ||
| 24 | i=1 | ||
| 25 | while test $i != X; do | ||
| 26 | t=111111111111111111111111111111111111111111111111111111111111111111111110$i | ||
| 27 | t=111111111111111111111111111111111111111111111111111111111111111111111111$i true | ||
| 28 | t=111111111111111111111111111111111111111111111111111111111111111111111112$i /bin/true | ||
| 29 | t=111111111111111111111111111111111111111111111111111111111111111111111113$i exec 1>&1 | ||
| 30 | i=1$i | ||
| 31 | if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi | ||
| 32 | if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi | ||
| 33 | if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi | ||
| 34 | if test $i = 1111111111111111111111111111111111111111111114; then i=X; fi | ||
| 35 | done | ||
| 36 | end=`ps -o pid,vsz | grep "^ *$pid "` | ||
| 37 | if test "$beg" != "$end"; then | ||
| 38 | true echo "vsz grows: $beg -> $end" | ||
| 39 | else | ||
| 40 | true echo "vsz does not grow" | ||
| 41 | fi | ||
| 42 | |||
| 43 | echo "Measuring memory leak..." | ||
| 44 | beg=`ps -o pid,vsz | grep "^ *$pid "` | ||
| 45 | i=1 | ||
| 46 | while test $i != X; do | ||
| 47 | t=111111111111111111111111111111111111111111111111111111111111111111111110$i | ||
| 48 | t=111111111111111111111111111111111111111111111111111111111111111111111111$i true | ||
| 49 | t=111111111111111111111111111111111111111111111111111111111111111111111112$i /bin/true | ||
| 50 | t=111111111111111111111111111111111111111111111111111111111111111111111113$i exec 1>&1 | ||
| 51 | i=1$i | ||
| 52 | if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi | ||
| 53 | if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi | ||
| 54 | if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi | ||
| 55 | if test $i = 1111111111111111111111111111111111111111111114; then i=X; fi | ||
| 56 | done | ||
| 57 | end=`ps -o pid,vsz | grep "^ *$pid "` | ||
| 58 | |||
| 59 | if test "$beg" != "$end"; then | ||
| 60 | echo "vsz grows: $beg -> $end" | ||
| 61 | else | ||
| 62 | echo "vsz does not grow" | ||
| 63 | fi | ||
