1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-13 11:42:21 +00:00

Fix crash on cd in subshell with PWD unset (re: 5ee290c)

Reproducer:

$ ksh -c 'unset PWD; (cd /); :'
Memory fault

The shell crashes because b_cd() is testing the value of the PWD
variable without checking if there is one.

src/cmd/ksh93/sh/path.c: path_pwd():
- Never return an unfreeable pointer to e_dot; always return a
  freeable pointer. This fixes another corner-case crashing bug.
- Make sure the PWD variable gets assigned a value if it doesn't
  have one, even if it's the "." fallback. However, if the PWD is
  inaccessible but we did inherit a $PWD value that starts with a
  /, then use the existing $PWD value as this will help the shell
  fail gracefully.

src/cmd/ksh93/bltins/cd_pwd.c:
- b_cd(): When checking if the PWD is valid, use the sh.pwd copy
  instead of the PWD variable. This fixes the crash above.
- b_cd(): Since path_pwd() now always returns a freeable value,
  free sh.pwd unconditionally before setting the new value.
- b_pwd(): Not only check that path_pwd() returns a value starting
  with a slash, but also verify it with test_inode() and error out
  if it's wrong. This makes the 'pwd' command useful for checking
  that the PWD is currently accessible.

src/cmd/ksh93/data/msg.c:
- Change e_pwd error message for accuracy and clarity.
This commit is contained in:
Martijn Dekker 2022-02-17 16:19:17 +00:00
parent d55e9686d7
commit 11177d448d
6 changed files with 46 additions and 21 deletions

5
NEWS
View file

@ -3,6 +3,11 @@ For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0
Any uppercase BUG_* names are modernish shell bug IDs. Any uppercase BUG_* names are modernish shell bug IDs.
2022-01-17:
- Fixed a crash, introduced on 2021-01-19, that occurred when using 'cd' in
a subshell with the PWD variable unset.
2022-01-16: 2022-01-16:
- Backported minor additions to the 'read' built-in command from ksh 93v-: - Backported minor additions to the 'read' built-in command from ksh 93v-:

View file

@ -100,8 +100,6 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
oldpwd = path_pwd(); oldpwd = path_pwd();
opwdnod = sh_scoped(OLDPWDNOD); opwdnod = sh_scoped(OLDPWDNOD);
pwdnod = sh_scoped(PWDNOD); pwdnod = sh_scoped(PWDNOD);
if(oldpwd == e_dot && pwdnod->nvalue.cp)
oldpwd = (char*)pwdnod->nvalue.cp; /* if path_pwd() failed to get the pwd, use $PWD */
if(sh.subshell) if(sh.subshell)
{ {
/* clone $OLDPWD and $PWD into the subshell's scope */ /* clone $OLDPWD and $PWD into the subshell's scope */
@ -126,7 +124,7 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
if(sh.subshell && !sh.subshare) if(sh.subshell && !sh.subshare)
{ {
#if _lib_fchdir #if _lib_fchdir
if(!test_inode(nv_getval(pwdnod),e_dot)) if(!test_inode(sh.pwd,e_dot))
#endif #endif
sh_subfork(); sh_subfork();
} }
@ -228,6 +226,7 @@ success:
if(*dp && (*dp!='.'||dp[1]) && strchr(dir,'/')) if(*dp && (*dp!='.'||dp[1]) && strchr(dir,'/'))
sfputr(sfstdout,dir,'\n'); sfputr(sfstdout,dir,'\n');
nv_putval(opwdnod,oldpwd,NV_RDONLY); nv_putval(opwdnod,oldpwd,NV_RDONLY);
free((void*)sh.pwd);
if(*dir == '/') if(*dir == '/')
{ {
size_t len = strlen(dir); size_t len = strlen(dir);
@ -236,16 +235,12 @@ success:
dir[len] = 0; dir[len] = 0;
nv_putval(pwdnod,dir,NV_RDONLY); nv_putval(pwdnod,dir,NV_RDONLY);
nv_onattr(pwdnod,NV_EXPORT); nv_onattr(pwdnod,NV_EXPORT);
if(sh.pwd) sh.pwd = sh_strdup(dir);
free((void*)sh.pwd);
sh.pwd = sh_strdup(pwdnod->nvalue.cp);
} }
else else
{ {
/* pathcanon() failed to canonicalize the directory, which happens when 'cd' is invoked from a /* pathcanon() failed to canonicalize the directory, which happens when 'cd' is invoked from a
nonexistent PWD with a relative path as the argument. Reinitialize $PWD as it will be wrong. */ nonexistent PWD with a relative path as the argument. Reinitialize $PWD as it will be wrong. */
if(sh.pwd)
free((void*)sh.pwd);
sh.pwd = NIL(const char*); sh.pwd = NIL(const char*);
path_pwd(); path_pwd();
if(*sh.pwd != '/') if(*sh.pwd != '/')
@ -291,7 +286,7 @@ int b_pwd(int argc, char *argv[],Shbltin_t *context)
errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
UNREACHABLE(); UNREACHABLE();
} }
if(*(cp = path_pwd()) != '/') if(*(cp = path_pwd()) != '/' || !test_inode(cp,e_dot))
{ {
errormsg(SH_DICT,ERROR_system(1), e_pwd); errormsg(SH_DICT,ERROR_system(1), e_pwd);
UNREACHABLE(); UNREACHABLE();

View file

@ -78,7 +78,7 @@ const char e_badpattern[] = "%s: invalid shell pattern";
const char e_noread[] = "%s: pattern seek requires read access"; const char e_noread[] = "%s: pattern seek requires read access";
const char e_logout[] = "Use 'exit' to terminate this shell"; const char e_logout[] = "Use 'exit' to terminate this shell";
const char e_exec[] = "%s: cannot execute"; const char e_exec[] = "%s: cannot execute";
const char e_pwd[] = "cannot access parent directories"; const char e_pwd[] = "cannot determine present working directory";
const char e_found[] = "%s: not found"; const char e_found[] = "%s: not found";
#ifdef ENAMETOOLONG #ifdef ENAMETOOLONG
const char e_toolong[] = "%s: file name too long"; const char e_toolong[] = "%s: file name too long";

View file

@ -21,7 +21,7 @@
#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */ #define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
#define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */ #define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */
#define SH_RELEASE_DATE "2022-02-16" /* must be in this format for $((.sh.version)) */ #define SH_RELEASE_DATE "2022-02-17" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_CPYR "(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK #define SH_RELEASE_CPYR "(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK
/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */ /* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */

View file

@ -186,7 +186,11 @@ char *path_pwd(void)
Namval_t *pwdnod; Namval_t *pwdnod;
/* Don't bother if PWD already set */ /* Don't bother if PWD already set */
if(sh.pwd) if(sh.pwd)
return((char*)sh.pwd); {
if(*sh.pwd=='/')
return((char*)sh.pwd);
free((void*)sh.pwd);
}
/* First see if PWD variable is correct */ /* First see if PWD variable is correct */
pwdnod = sh_scoped(PWDNOD); pwdnod = sh_scoped(PWDNOD);
cp = nv_getval(pwdnod); cp = nv_getval(pwdnod);
@ -198,22 +202,29 @@ char *path_pwd(void)
cp = nv_getval(sh_scoped(HOME)); cp = nv_getval(sh_scoped(HOME));
if(!(cp && *cp=='/' && test_inode(cp,e_dot))) if(!(cp && *cp=='/' && test_inode(cp,e_dot)))
{ {
/* Get physical PWD (no symlinks) using getcwd(3), fall back to "." */ /* Get physical PWD (no symlinks) using getcwd(3) */
cp = sh_getcwd(); cp = sh_getcwd();
if(!cp) if(cp)
return((char*)e_dot); tofree++;
tofree++;
} }
/* Store in PWD variable */ /* Store in PWD variable */
if(sh.subshell) if(cp)
pwdnod = sh_assignok(pwdnod,1); {
nv_putval(pwdnod,cp,NV_RDONLY); if(sh.subshell)
pwdnod = sh_assignok(pwdnod,1);
nv_putval(pwdnod,cp,NV_RDONLY);
}
if(tofree) if(tofree)
free(cp); free((void*)cp);
} }
nv_onattr(pwdnod,NV_EXPORT); nv_onattr(pwdnod,NV_EXPORT);
/* Neither obtained the pwd nor can fall back to sane-ish $PWD: fall back to "." */
if(!cp)
cp = nv_getval(pwdnod);
if(!cp || *cp!='/')
nv_putval(pwdnod,cp=(char*)e_dot,NV_RDONLY);
/* Set shell PWD */ /* Set shell PWD */
sh.pwd = sh_strdup(pwdnod->nvalue.cp); sh.pwd = sh_strdup(cp);
return((char*)sh.pwd); return((char*)sh.pwd);
} }

View file

@ -931,5 +931,19 @@ then (
) 2>/dev/null || err_exit "'source' in POSIX mode does not find ksh function" ) 2>/dev/null || err_exit "'source' in POSIX mode does not find ksh function"
fi fi
# ======
# Crash after unsetting PWD
(unset PWD; (cd /); :) & # the : avoids optimizing out the subshell
wait "$!" 2>/dev/null
((!(e = $?))) || err_exit "shell crashes on 'cd' in subshell exit with unset PWD" \
"(got status $e$( ((e>128)) && print -n /SIG && kill -l "$e"))"
mkdir "$tmp/testdir"
cd "$tmp/testdir"
"$SHELL" -c 'cd /; rmdir "$1"' x "$tmp/testdir"
(unset PWD; exec "$SHELL" -c '(cd /); :') &
wait "$!" 2>/dev/null
((!(e = $?))) || err_exit 'shell crashes on failure obtain the PWD on init' \
"(got status $e$( ((e>128)) && print -n /SIG && kill -l "$e"))"
# ====== # ======
exit $((Errors<125?Errors:125)) exit $((Errors<125?Errors:125))