Repository navigation
Commit a0a56a4
authored
Fix memleak invoking functions with trap set in parent scope (#948)
Vincent Mihalkovič (@vmihalko) from Red Hat reports:
> ksh leaks memory when running a loop that combines trap and
> command substitution. Without the trap or command substitution,
> the loop does not leak. The leak is reproducible on upstream
> HEAD.
>
> ### Reproducer
>
> trap exit INT
>
> n=10000
> while [ "$n" -gt 0 ]; do
> value=`echo a`
> n=$((n - 1))
> done
> exit 0
>
> Steps:
>
> 1. Save as e.g. `reproducer_limited.ksh` and make executable:
> chmod +x reproducer_limited.ksh
> 2. Run under valgrind:
> valgrind --leak-check=full -q /path/to/ksh \
> reproducer_limited.ksh
> 3. Inspect valgrind output for the LEAK SUMMARY and
> "definitely lost" lines.
>
> Valgrind reports "definitely lost" blocks from the
> trap/command-substitution path. Example (upstream HEAD):
>
> * 49,995 bytes in 9,999 blocks are definitely lost
> * Stack points to: malloc → _ast_strdup → sh_strdup (init.c)
> → sh_subshell (subshell.c) → comsubst (macro.c) → …
>
> Without the 'trap exit INT' line, the same loop does not show
> "definitely lost" (valgrind is clean for that path); the trap is
> required to trigger the leak.
>
> ### References
>
> * Downstream: RHEL-19580 – ksh leak with traps
> https://issues.redhat.com/browse/RHEL-19580
> (Red Hat Jira). Affects: rhel-7.9, rhel-8.8.0, rhel-9.4.
> * Related:
> - Bugzilla 1460940
> https://bugzilla.redhat.com/show_bug.cgi?id=1460940
> - Bugzilla 1117404
> https://bugzilla.redhat.com/show_bug.cgi?id=1117404
> (trap/crash context).
Analysis:
The Red Hat patch backported back in 2020, in 6193c6a, is not
correct. The original 93u+ code mostly made sense; there is no
reason why saved trap action pointers should change in subshells or
ksh function invocations, because we're creating a new scope and
any changes in traps apply to the new scope only, so the original
pointers should never be invalidated. Any such occurrence is a bug.
The Red Hat patch doesn't actually fix any such bug; it papers over
it at the expense of introducing a memory leak problem. So we have
to revert it, then figure out what the actual bug is and find a
proper fix for that bug.
src/cmd/ksh93/sh/subshell.c,
src/cmd/ksh93/sh/xec.c:
- Mostly revert to the 93u+ method for saving/restoring trap table,
which simply copies over the array of pointers.
- Avoid reintroducing the off-by-one error that was fixed in
3aee10d and which carried over into the Red Hat patch.
- sh_subshell(): Allocate the buffer for saving the trap action
pointers on the AST stack, which is automatically reset
appropriately at the end of every recursive sh_exec() run, which
removes the need to free the buffer manually while guaranteeing
the buffer stays available while we need it. (Note that
sh_funscope() was already using this method in 93u+.)
With these changes applied, the memory leak problem disappears, and
the regression tests appear to pass normally. However, when
compiling with AddressSanitizer, the patch fails one of the two
regression tests that were introduced by referenced commit, the one
that tests a ksh function. This is not unexpected. The failing test
is at src/cmd/ksh93/tests/functions.sh, lines 1368-1383.
(The other one, at subshell.sh lines 932-943, passes, because as of
3d1a472, ksh always forks a subshell when it traps a signal, which
circumvents the bug.)
The regression is caused by a use after free. The offending free
call is in b_trap(), line 180. That code is essentially unchanged
since 93u+. This use after free appears to be the very problem that
the Red Hat patch from the referenced commit works around.
So we need a new and correct solution for that use after free. It
occurs when a trap for a signal is set or ignored in the parent
environment and is then set again in a ksh function. The function
then needs to be called at twice for the use after free to occur.
Reproducer:
trap "" HUP
function ksh_fun
{
trap ": foo" HUP
trap ": bar" HUP
}
ksh_fun
ksh_fun
A simplistic fix would be to avoid the free call at trap.c:180 if
we're currently inside a ksh function. But that would introduce a
memory leak when the same signal is trapped locally more than once
in the same ksh function.
The challenge is this: The first time a trap is set in a ksh
function scope, the parent trap should not be freed, because it
will be restored. However, the second and subsequent times, it
should be freed, because it's overwriting the first one. The
following changes make that happen.
src/cmd/ksh93/include/shell.h,
src/cmd/ksh93/bltins/trap.c,
src/cmd/ksh93/sh/subshell.c,
src/cmd/ksh93/sh/xec.c:
- Introduce a sh.st.trapnofree bitmask array, stored in the current
scope sh.st, which contains a bit for every possible signal. By
default, all those bits are zero and traps are freed as normal.
- Use the NSIG macro (which indicates the number of signals
supported by the system; non-standard but available on all major
systems), to determine the size of the bitmask array if possible.
- sh_subshell(), sh_funscope(), sh_exec(): When saving the array of
trap action pointers, set all trapnofree bits to one.
- b_trap(): Whenever the trap built-in would free a previous
(potentially parent) trap, skip the free call if the
corresponding bit is set, but then (whether a free was actually
executed or not) clear that bit, so that if the same signal trap
is overwritten within the same scope, the free is executed as
normal. This fixes the use after free while avoiding the
introduction of another memory leak.
While we're here, let's fix another, more minor problem: the shell
unnecessarily forks when setting an EXIT trap, as EXIT is the one
pseudosignal that is handled in the same array as the signal traps.
src/cmd/ksh93/bltins/trap.c: b_trap():
- Since EXIT == 0, check for sig > 0 before forking. (re: 3d1a472)
Resolves: #9471 parent ae5a6ff commit a0a56a4
7 files changed
Lines changed: 68 additions & 81 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
5 | 14 | | |
6 | 15 | | |
7 | 16 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
164 | 164 | | |
165 | 165 | | |
166 | 166 | | |
| 167 | + | |
| 168 | + | |
167 | 169 | | |
168 | | - | |
169 | | - | |
| 170 | + | |
| 171 | + | |
170 | 172 | | |
171 | 173 | | |
172 | | - | |
| 174 | + | |
173 | 175 | | |
174 | 176 | | |
175 | 177 | | |
176 | 178 | | |
177 | 179 | | |
178 | 180 | | |
179 | | - | |
| 181 | + | |
| 182 | + | |
180 | 183 | | |
| 184 | + | |
| 185 | + | |
181 | 186 | | |
182 | 187 | | |
183 | 188 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
194 | 194 | | |
195 | 195 | | |
196 | 196 | | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
197 | 203 | | |
198 | 204 | | |
199 | 205 | | |
| |||
225 | 231 | | |
226 | 232 | | |
227 | 233 | | |
| 234 | + | |
228 | 235 | | |
229 | 236 | | |
230 | 237 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
525 | 525 | | |
526 | 526 | | |
527 | 527 | | |
528 | | - | |
| 528 | + | |
529 | 529 | | |
530 | 530 | | |
531 | 531 | | |
532 | 532 | | |
| 533 | + | |
533 | 534 | | |
534 | 535 | | |
535 | 536 | | |
| |||
597 | 598 | | |
598 | 599 | | |
599 | 600 | | |
600 | | - | |
| 601 | + | |
601 | 602 | | |
602 | 603 | | |
603 | 604 | | |
| |||
618 | 619 | | |
619 | 620 | | |
620 | 621 | | |
621 | | - | |
622 | | - | |
623 | | - | |
624 | | - | |
625 | | - | |
626 | | - | |
627 | | - | |
628 | | - | |
629 | | - | |
630 | | - | |
631 | | - | |
632 | | - | |
633 | | - | |
634 | | - | |
635 | | - | |
636 | | - | |
637 | | - | |
638 | | - | |
639 | | - | |
640 | | - | |
641 | | - | |
642 | | - | |
643 | | - | |
644 | 622 | | |
645 | 623 | | |
646 | 624 | | |
647 | 625 | | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
648 | 633 | | |
649 | 634 | | |
650 | 635 | | |
| |||
805 | 790 | | |
806 | 791 | | |
807 | 792 | | |
808 | | - | |
809 | 793 | | |
810 | 794 | | |
811 | 795 | | |
| |||
862 | 846 | | |
863 | 847 | | |
864 | 848 | | |
865 | | - | |
866 | | - | |
867 | | - | |
868 | | - | |
869 | | - | |
870 | | - | |
871 | | - | |
| 849 | + | |
872 | 850 | | |
873 | 851 | | |
874 | 852 | | |
| |||
957 | 935 | | |
958 | 936 | | |
959 | 937 | | |
960 | | - | |
| 938 | + | |
961 | 939 | | |
962 | | - | |
963 | | - | |
| 940 | + | |
| 941 | + | |
964 | 942 | | |
965 | 943 | | |
966 | 944 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1718 | 1718 | | |
1719 | 1719 | | |
1720 | 1720 | | |
1721 | | - | |
1722 | | - | |
1723 | | - | |
| 1721 | + | |
| 1722 | + | |
| 1723 | + | |
1724 | 1724 | | |
1725 | | - | |
1726 | | - | |
1727 | | - | |
1728 | | - | |
1729 | | - | |
1730 | | - | |
1731 | | - | |
1732 | | - | |
1733 | | - | |
| 1725 | + | |
| 1726 | + | |
| 1727 | + | |
| 1728 | + | |
| 1729 | + | |
| 1730 | + | |
| 1731 | + | |
| 1732 | + | |
1734 | 1733 | | |
1735 | 1734 | | |
1736 | | - | |
| 1735 | + | |
1737 | 1736 | | |
1738 | 1737 | | |
1739 | 1738 | | |
| |||
2900 | 2899 | | |
2901 | 2900 | | |
2902 | 2901 | | |
2903 | | - | |
2904 | 2902 | | |
2905 | 2903 | | |
2906 | 2904 | | |
2907 | 2905 | | |
2908 | | - | |
| 2906 | + | |
2909 | 2907 | | |
2910 | 2908 | | |
2911 | 2909 | | |
2912 | 2910 | | |
| 2911 | + | |
2913 | 2912 | | |
2914 | 2913 | | |
2915 | 2914 | | |
| |||
2987 | 2986 | | |
2988 | 2987 | | |
2989 | 2988 | | |
2990 | | - | |
2991 | | - | |
2992 | | - | |
2993 | | - | |
2994 | | - | |
2995 | | - | |
2996 | | - | |
2997 | | - | |
2998 | | - | |
2999 | | - | |
3000 | | - | |
3001 | | - | |
3002 | | - | |
3003 | | - | |
3004 | | - | |
3005 | | - | |
3006 | | - | |
3007 | | - | |
| 2989 | + | |
| 2990 | + | |
| 2991 | + | |
3008 | 2992 | | |
3009 | 2993 | | |
3010 | 2994 | | |
| |||
3119 | 3103 | | |
3120 | 3104 | | |
3121 | 3105 | | |
3122 | | - | |
3123 | | - | |
3124 | | - | |
3125 | | - | |
3126 | | - | |
3127 | | - | |
3128 | | - | |
| 3106 | + | |
3129 | 3107 | | |
3130 | 3108 | | |
3131 | 3109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
456 | 456 | | |
457 | 457 | | |
458 | 458 | | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
459 | 469 | | |
460 | 470 | | |
0 commit comments