Skip to content

Commit fe20311

Browse files
committed
Fix command substitution memory leaks (rhbz#982142)
This fixes two memory leaks in old-style command substitutions (one when invoking an alias, one when invoking an autoloaded function), as well as a possible third leak with an unknown reproducer, by applying this Red Hat patch: https://src.fedoraproject.org/rpms/ksh/blob/642af4d6/f/ksh-20120801-mlikfiks.patch src/cmd/ksh93/sh/macro.c: comsubst(): - For as-yet unknown reasons, the alias leak did not occur when adding a space at the end of the command substitution, as in a=`some_alias `. This fix is a workaround that simply writes an extra space to the stack. TODO: a real fix. src/cmd/ksh93/sh/path.c: funload(): - Add missing free() before return. This fixes the leak with autoloaded functions. src/cmd/ksh93/sh/lex.c: alias_exceptf(): - This function is called "whenever an end of string is found with alias". This adds a check for an SF_FINAL stream status flag when deciding whether to call free(). In sfio.h this is commented as: #define SF_FINAL 11 /* closing is done except stream free */ When I revert this change, none of the regression tests fail, so I don't know how to trigger this supposed leak. But it makes some sense given the sfio.h comment, so I'll keep it. src/cmd/ksh93/tests/leaks.sh: - Add the reproducers from rhbz#982142 as regression tests (including an extra one for nested command substitutions that was already fixed as of 93u+, but testing is good). I replaced the external 'expr' and 'ls' commands by uses of the 'true' builtin, otherwise the tests take far too long to run with 16384 iterations. At least the alias leak was still behaving identically after replacing 'ls' by 'true'.
1 parent e661191 commit fe20311

4 files changed

Lines changed: 43 additions & 1 deletion

File tree

‎src/cmd/ksh93/sh/lex.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2445,7 +2445,7 @@ static int alias_exceptf(Sfio_t *iop,int type,Sfdisc_t *handle)
24452445
if(dp!=handle)
24462446
sfdisc(iop,dp);
24472447
}
2448-
else if(type==SF_FINAL)
2448+
else if(type==SF_DPOP || type==SF_FINAL)
24492449
free((void*)ap);
24502450
goto done;
24512451
}

‎src/cmd/ksh93/sh/macro.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2110,6 +2110,7 @@ static void comsubst(Mac_t *mp,register Shnode_t* t, int type)
21102110
}
21112111
sfputc(stkp,c);
21122112
}
2113+
sfputc(stkp,' '); /* rhbz#982142: a=`some_alias` leaked memory, a=`some_alias ` did not! TODO: non-hack fix */
21132114
c = stktell(stkp);
21142115
str=stkfreeze(stkp,1);
21152116
/* disable verbose and don't save in history file */

‎src/cmd/ksh93/sh/path.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,7 @@ static void funload(Shell_t *shp,int fno, const char *name)
612612
}
613613
while((rp=dtnext(shp->fpathdict,rp)) && strcmp(pname,rp->fname)==0);
614614
sh_close(fno);
615+
free((void*)pname);
615616
return;
616617
}
617618
sh_onstate(SH_NOLOG);

‎src/cmd/ksh93/tests/leaks.sh‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,5 +270,45 @@ done
270270
after=$(getmem)
271271
err_exit_if_leak 'unalias'
272272

273+
# ======
274+
# Red Hat bug rhbz#982142: command substitution leaks
275+
276+
# case1: Nested command substitutions
277+
# (reportedly already fixed in 93u+, but let's keep the test)
278+
before=$(getmem)
279+
for ((i=0; i < N; i++))
280+
do a=`true 1 + \`true 1 + 1\`` # was: a=`expr 1 + \`expr 1 + 1\``
281+
done
282+
after=$(getmem)
283+
err_exit_if_leak 'nested command substitutions'
284+
285+
# case2: Command alias
286+
alias ls='true -ltr' # was: alias ls='ls -ltr'
287+
before=$(getmem)
288+
for ((i=0; i < N; i++))
289+
do eval 'a=`ls`'
290+
done
291+
after=$(getmem)
292+
unalias ls
293+
err_exit_if_leak 'alias in command substitution'
294+
295+
# case3: Function call via autoload
296+
cat >$tmp/func1 <<\EOF
297+
function func1
298+
{
299+
echo "func1 call";
300+
}
301+
EOF
302+
FPATH=$tmp
303+
autoload func1
304+
before=$(getmem)
305+
for ((i=0; i < N; i++))
306+
do a=`func1`
307+
done
308+
after=$(getmem)
309+
unset -f func1
310+
unset -v FPATH
311+
err_exit_if_leak 'function call via autoload in command substitution'
312+
273313
# ======
274314
exit $((Errors<125?Errors:125))

0 commit comments

Comments
 (0)