mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
autoload: Add loop detection
It was trivial to crash ksh by making an autoloaded function
definition file autoload itself, causing a stack overflow due to
infinite recursion. This commit adds loop detection that stops a
function that is being autoloaded from autoloading itself either
directly or indirectly, without removing the ability of autoloaded
function definition files to autoload other functions.
src/cmd/ksh93/sh/path.c: funload():
- Detect loops by checking if the path of a function to be
autoloaded was already added to a new internal static tree,
and if not, adding it while the function is being loaded.
src/cmd/ksh93/tests/path.sh:
- Add regression test.
- Tweak a couple of others to be freeze- and crash-proof.
NEWS:
- Add this fix + a forgotten entry for the previous fix (6f3b23e6
).
Fixes: https://github.com/ksh93/ksh/issues/136
This commit is contained in:
parent
6f3b23e6f4
commit
a410bc480f
2 changed files with 55 additions and 5 deletions
|
@ -577,7 +577,8 @@ char *path_fullname(Shell_t *shp,const char *name)
|
||||||
static void funload(Shell_t *shp,int fno, const char *name)
|
static void funload(Shell_t *shp,int fno, const char *name)
|
||||||
{
|
{
|
||||||
char *pname,*oldname=shp->st.filename, buff[IOBSIZE+1];
|
char *pname,*oldname=shp->st.filename, buff[IOBSIZE+1];
|
||||||
Namval_t *np;
|
Namval_t *np, *np_loopdetect;
|
||||||
|
static Dt_t *loopdetect_tree;
|
||||||
struct Ufunction *rp,*rpfirst;
|
struct Ufunction *rp,*rpfirst;
|
||||||
int savestates = sh_getstate(), oldload=shp->funload, savelineno = shp->inlineno;
|
int savestates = sh_getstate(), oldload=shp->funload, savelineno = shp->inlineno;
|
||||||
pname = path_fullname(shp,stakptr(PATH_OFFSET));
|
pname = path_fullname(shp,stakptr(PATH_OFFSET));
|
||||||
|
@ -607,6 +608,11 @@ static void funload(Shell_t *shp,int fno, const char *name)
|
||||||
free((void*)pname);
|
free((void*)pname);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(!loopdetect_tree)
|
||||||
|
loopdetect_tree = dtopen(&_Nvdisc,Dtoset);
|
||||||
|
else if(nv_search(pname,loopdetect_tree,0))
|
||||||
|
errormsg(SH_DICT,ERROR_exit(ERROR_NOEXEC),"autoload loop: %s in %s",name,pname);
|
||||||
|
np_loopdetect = nv_search(pname,loopdetect_tree,NV_ADD);
|
||||||
sh_onstate(SH_NOALIAS);
|
sh_onstate(SH_NOALIAS);
|
||||||
shp->readscript = (char*)name;
|
shp->readscript = (char*)name;
|
||||||
shp->st.filename = pname;
|
shp->st.filename = pname;
|
||||||
|
@ -631,6 +637,7 @@ static void funload(Shell_t *shp,int fno, const char *name)
|
||||||
shp->inlineno = savelineno;
|
shp->inlineno = savelineno;
|
||||||
shp->st.filename = oldname;
|
shp->st.filename = oldname;
|
||||||
sh_setstate(savestates);
|
sh_setstate(savestates);
|
||||||
|
nv_delete(np_loopdetect,loopdetect_tree,0);
|
||||||
if(pname)
|
if(pname)
|
||||||
errormsg(SH_DICT,ERROR_exit(ERROR_NOEXEC),e_funload,name,pname);
|
errormsg(SH_DICT,ERROR_exit(ERROR_NOEXEC),e_funload,name,pname);
|
||||||
}
|
}
|
||||||
|
|
|
@ -476,6 +476,7 @@ actual=$(PATH=/dev/null "$SHELL" -c 'command -p ls /dev/null' 2>&1)
|
||||||
[[ $actual == "$expect" ]] || err_exit 'command -p fails to find standard utility' \
|
[[ $actual == "$expect" ]] || err_exit 'command -p fails to find standard utility' \
|
||||||
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
|
"(expected $(printf %q "$expect"), got $(printf %q "$actual"))"
|
||||||
|
|
||||||
|
# ======
|
||||||
# ksh segfaults if $PATH contains a .paths directory
|
# ksh segfaults if $PATH contains a .paths directory
|
||||||
mkdir -p $tmp/paths-dir-crash/
|
mkdir -p $tmp/paths-dir-crash/
|
||||||
cat > $tmp/paths-dir-crash/run.sh <<- EOF
|
cat > $tmp/paths-dir-crash/run.sh <<- EOF
|
||||||
|
@ -483,8 +484,21 @@ mkdir -p $tmp/paths-dir-crash/.paths
|
||||||
export PATH=$tmp/paths-dir-crash:$PATH
|
export PATH=$tmp/paths-dir-crash:$PATH
|
||||||
print ok
|
print ok
|
||||||
EOF
|
EOF
|
||||||
[[ $($SHELL $tmp/paths-dir-crash/run.sh 2>/dev/null) == ok ]] || err_exit "ksh crashes if PATH contains a .paths directory"
|
ofile=$tmp/dotpaths.out
|
||||||
|
trap 'sleep_pid=; while kill -9 $pid; do :; done 2>/dev/null; err_exit "PATH containing .paths directory: shell hung"' TERM
|
||||||
|
trap 'kill $sleep_pid; while kill -9 $pid; do :; done 2>/dev/null; trap - INT; kill -s INT $$"' INT
|
||||||
|
{ sleep 5; kill $$; } &
|
||||||
|
sleep_pid=$!
|
||||||
|
"$SHELL" "$tmp/paths-dir-crash/run.sh" >$ofile 2>&1 &
|
||||||
|
pid=$!
|
||||||
|
{ wait $pid; } 2>>$ofile
|
||||||
|
e=$?
|
||||||
|
trap - TERM INT
|
||||||
|
[[ $sleep_pid ]] && kill $sleep_pid
|
||||||
|
((!e)) && [[ $(<$ofile) == ok ]] || err_exit "PATH containing .paths directory:" \
|
||||||
|
"got status $e$( ((e>128)) && print -n / && kill -l "$e"), $(printf %q "$(<$ofile)")"
|
||||||
|
|
||||||
|
# ======
|
||||||
# Check that 'command -p' and 'command -p -v' do not use the hash table (a.k.a. tracked aliases).
|
# Check that 'command -p' and 'command -p -v' do not use the hash table (a.k.a. tracked aliases).
|
||||||
print 'echo "wrong path used"' > $tmp/ls
|
print 'echo "wrong path used"' > $tmp/ls
|
||||||
chmod +x $tmp/ls
|
chmod +x $tmp/ls
|
||||||
|
@ -523,6 +537,7 @@ then
|
||||||
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ======
|
||||||
# 'command -x' used to hang in an endless E2BIG loop on Linux and macOS
|
# 'command -x' used to hang in an endless E2BIG loop on Linux and macOS
|
||||||
ofile=$tmp/command_x_chunks.sh
|
ofile=$tmp/command_x_chunks.sh
|
||||||
trap 'sleep_pid=; while kill -9 $pid; do :; done 2>/dev/null; err_exit "'\''command -x'\'' hung"' TERM
|
trap 'sleep_pid=; while kill -9 $pid; do :; done 2>/dev/null; err_exit "'\''command -x'\'' hung"' TERM
|
||||||
|
@ -681,9 +696,37 @@ fi
|
||||||
# ======
|
# ======
|
||||||
# Very long nonexistent command names used to crash
|
# Very long nonexistent command names used to crash
|
||||||
# https://github.com/ksh93/ksh/issues/144
|
# https://github.com/ksh93/ksh/issues/144
|
||||||
{ PATH=/dev/null FPATH=/dev/null "$SHELL" -c "$(awk -v ORS= 'BEGIN { for(i=0;i<10000;i++) print "xxxxxxxxxx"; }')"; } 2>/dev/null
|
ofile=$tmp/longname.err
|
||||||
(((e = $?) == 127)) || err_exit "Long nonexistent command name crashes shell" \
|
trap 'sleep_pid=; while kill -9 $pid; do :; done 2>/dev/null; err_exit "Long nonexistent command name: shell hung"' TERM
|
||||||
"(exit status $e$( ((e>128)) && print -n / && kill -l "$e"))"
|
trap 'kill $sleep_pid; while kill -9 $pid; do :; done 2>/dev/null; trap - INT; kill -s INT $$"' INT
|
||||||
|
{ sleep 5; kill $$; } &
|
||||||
|
sleep_pid=$!
|
||||||
|
PATH=/dev/null FPATH=/dev/null "$SHELL" -c "$(awk -v ORS= 'BEGIN { for(i=0;i<10000;i++) print "xxxxxxxxxx"; }')" 2>/dev/null &
|
||||||
|
pid=$!
|
||||||
|
{ wait $pid; } 2>$ofile
|
||||||
|
e=$?
|
||||||
|
trap - TERM INT
|
||||||
|
[[ $sleep_pid ]] && kill $sleep_pid
|
||||||
|
((e == 127)) || err_exit "Long nonexistent command name:" \
|
||||||
|
"got status $e$( ((e>128)) && print -n / && kill -l "$e"), $(printf %q "$(<$ofile)")"
|
||||||
|
|
||||||
|
# ======
|
||||||
|
# A function autoload recursion loop used to crash
|
||||||
|
# https://github.com/ksh93/ksh/issues/136
|
||||||
|
mkdir "$tmp/fun.$$" && cd "$tmp/fun.$$" || exit
|
||||||
|
echo 'self2' >self
|
||||||
|
echo 'self3' >self2
|
||||||
|
echo 'self4' >self3
|
||||||
|
echo 'self' >self4
|
||||||
|
cd ~- || exit
|
||||||
|
exp="$SHELL: line 2: autoload loop: self in $tmp/fun.$$/self
|
||||||
|
$SHELL: function, built-in or type definition for self4 not found in $tmp/fun.$$/self4
|
||||||
|
$SHELL: function, built-in or type definition for self3 not found in $tmp/fun.$$/self3
|
||||||
|
$SHELL: function, built-in or type definition for self2 not found in $tmp/fun.$$/self2
|
||||||
|
$SHELL: function, built-in or type definition for self not found in $tmp/fun.$$/self"
|
||||||
|
got=$({ FPATH=$tmp/fun.$$ "$SHELL" -c self; } 2>&1)
|
||||||
|
(((e = $?) == 126)) || err_exit 'Function autoload recursion loop:' \
|
||||||
|
"got status $e$( ((e>128)) && print -n / && kill -l "$e"), $(printf %q "$got")"
|
||||||
|
|
||||||
# ======
|
# ======
|
||||||
exit $((Errors<125?Errors:125))
|
exit $((Errors<125?Errors:125))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue