1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

Yet more fixes for subshell directory handling (re: feaf718f)

There were still problems left after the previous commit. On at
least one system (QNX i386), the following regression test crashed:

src/cmd/ksh93/test/subshell.c
900     got=$( { "$SHELL" -c '(cd /; (cd /)); print -r -- "PWD=$PWD"'; } 2>&1 )

A backtrace done on the core dunp pointed to the free() call here:

src/cmd/ksh93/bltins/cd_pwd.c
90      if(oldpwd && oldpwd!=shp->pwd && oldpwd!=e_dot)
91              free(oldpwd);

Analysis: The interaction between $PWD, sh.pwd aka shp->pwd, and
the path_pwd() function is a mess. path_pwd() usually returns a
freeable value, but not always. sh.pwd is sometimes a pointer to
the value of $PWD, but not always (e.g. when you unset PWD or
assign to it). Instead of debugging the exact cause of the crash, I
think it is better to make this work in a more consistent way.

As of this commit:
1. sh.pwd keeps its own copy of the PWD, independently of the PWD
   variable. The old value must always be freed immediately before
   assigning a new one. This is simple and consistent, reducing the
   chance of bugs at negligible cost.
2. The PWD variable is no longer given the NV_NOFREE attribute
   because its value no longer points to sh.pwd. It is now a
   variable like any other.

src/cmd/ksh93/sh/path.c: path_pwd():
- Do not give PWDNOD the NV_NOFREE attribute.
- Give sh.pwd its own copy of the PWD by strdup'ing PWDNOD's value.

src/cmd/ksh93/bltins/cd_pwd.c: b_cd():
- Since sh.pwd is now consistently freed before giving it a new
  value and at no other time, oldpwd must not be freed any longer
  and can become a regular non-static variable.
- If the PWD needs reinitialising, call path_pwd() to do it.

src/cmd/ksh93/sh/subshell.c: sh_subshell():
- Systems with fchdir(2): Always restore the PWD upon exiting a
  non-subshare subshell. The check to decide whether or not to
  restore it was unsafe: it was not restored if the current PWD
  pointer and value was identical to the saved one, but a directory
  can be deleted and recreated under the same name.
- Systems without fchdir(2) (if any exist):
  . Entry: Fork if the PWD is nonexistent or has no x permission.
  . Restore: Only chdir back if the subshell PWD was changed.
  That's probably the best we can do. It remains inherently unsafe.
  We should probably just require fchdir(2) at some point.
This commit is contained in:
Martijn Dekker 2021-04-20 05:39:10 +01:00
parent feaf718f16
commit 61e0f90460
3 changed files with 41 additions and 43 deletions

View file

@ -256,6 +256,7 @@ char *path_pwd(Shell_t *shp,int flag)
{
/* Check if $HOME is a path to the PWD; this ensures $PWD == $HOME
at login, even if $HOME is a path that contains symlinks */
char tofree = 0;
cp = nv_getval(sh_scoped(shp,HOME));
if(!(cp && *cp=='/' && test_inode(cp,e_dot)))
{
@ -263,17 +264,19 @@ char *path_pwd(Shell_t *shp,int flag)
cp = getcwd(NIL(char*),0);
if(!cp)
return((char*)e_dot);
tofree++;
}
/* Store in PWD variable */
if(shp->subshell)
pwdnod = sh_assignok(pwdnod,1);
nv_offattr(pwdnod,NV_NOFREE);
nv_putval(pwdnod,cp,NV_RDONLY);
if(tofree)
free(cp);
}
nv_onattr(pwdnod,NV_NOFREE|NV_EXPORT);
nv_onattr(pwdnod,NV_EXPORT);
/* Set shell PWD */
shp->pwd = (char*)(pwdnod->nvalue.cp);
return(cp);
shp->pwd = sh_strdup(pwdnod->nvalue.cp);
return((char*)shp->pwd);
}
/*