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:
parent
feaf718f16
commit
61e0f90460
3 changed files with 41 additions and 43 deletions
|
@ -57,7 +57,7 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
|
|||
register Shell_t *shp = context->shp;
|
||||
int saverrno=0;
|
||||
int rval,flag=0;
|
||||
static char *oldpwd;
|
||||
char *oldpwd;
|
||||
Namval_t *opwdnod, *pwdnod;
|
||||
if(sh_isoption(SH_RESTRICTED))
|
||||
{
|
||||
|
@ -87,8 +87,6 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
|
|||
errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
|
||||
UNREACHABLE();
|
||||
}
|
||||
if(oldpwd && oldpwd!=shp->pwd && oldpwd!=e_dot)
|
||||
free(oldpwd);
|
||||
oldpwd = path_pwd(shp,0);
|
||||
opwdnod = sh_scoped(shp,OLDPWDNOD);
|
||||
pwdnod = sh_scoped(shp,PWDNOD);
|
||||
|
@ -229,25 +227,25 @@ success:
|
|||
while(--flag>0 && dir[flag]=='/')
|
||||
dir[flag] = 0;
|
||||
nv_putval(pwdnod,dir,NV_RDONLY);
|
||||
nv_onattr(pwdnod,NV_EXPORT);
|
||||
if(shp->pwd)
|
||||
free((void*)shp->pwd);
|
||||
shp->pwd = sh_strdup(pwdnod->nvalue.cp);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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. */
|
||||
char *cp = getcwd(NIL(char*),0);
|
||||
if(cp)
|
||||
{
|
||||
nv_putval(pwdnod,cp,NV_RDONLY);
|
||||
free(cp);
|
||||
}
|
||||
else
|
||||
if(shp->pwd)
|
||||
free((void*)shp->pwd);
|
||||
shp->pwd = NIL(const char*);
|
||||
path_pwd(shp,0);
|
||||
if(*shp->pwd != '/')
|
||||
{
|
||||
errormsg(SH_DICT,ERROR_system(1),e_direct);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
nv_onattr(pwdnod,NV_NOFREE|NV_EXPORT);
|
||||
shp->pwd = pwdnod->nvalue.cp;
|
||||
nv_scan(sh_subtracktree(1),rehash,(void*)0,NV_TAGGED,NV_TAGGED);
|
||||
path_newdir(shp,shp->pathlist);
|
||||
path_newdir(shp,shp->cdpathlist);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -82,7 +82,6 @@ static struct subshell
|
|||
pid_t subpid; /* child process id */
|
||||
Sfio_t* saveout;/* saved standard output */
|
||||
char *pwd; /* present working directory */
|
||||
const char *shpwd; /* saved pointer to sh.pwd */
|
||||
void *jobs; /* save job info */
|
||||
mode_t mask; /* saved umask */
|
||||
short tmpfd; /* saved tmp file descriptor */
|
||||
|
@ -213,7 +212,6 @@ void sh_subfork(void)
|
|||
else
|
||||
{
|
||||
/* this is the child part of the fork */
|
||||
/* setting subpid to 1 causes subshell to exit when reached */
|
||||
sh_onstate(SH_FORKED);
|
||||
subshell_data = 0;
|
||||
shp->subshell = 0;
|
||||
|
@ -561,9 +559,6 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
|
|||
path_get(shp,e_dot);
|
||||
shp->pathinit = 0;
|
||||
}
|
||||
#if _lib_fchdir
|
||||
sp->pwdfd = -1;
|
||||
#endif /* _lib_fchdir */
|
||||
if(!shp->pwd)
|
||||
path_pwd(shp,0);
|
||||
sp->bckpid = shp->bckpid;
|
||||
|
@ -584,8 +579,8 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
|
|||
if(!shp->subshare)
|
||||
{
|
||||
struct subshell *xp;
|
||||
sp->shpwd = shp->pwd;
|
||||
#if _lib_fchdir
|
||||
sp->pwdfd = -1;
|
||||
for(xp=sp->prev; xp; xp=xp->prev)
|
||||
{
|
||||
if(xp->pwdfd>0 && xp->pwd && strcmp(xp->pwd,shp->pwd)==0)
|
||||
|
@ -687,6 +682,17 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
|
|||
#if _lib_fchdir
|
||||
if(sp->pwdfd < 0 && !shp->subshare) /* if we couldn't get a file descriptor to our PWD ... */
|
||||
sh_subfork(); /* ...we have to fork, as we cannot fchdir back to it. */
|
||||
#else
|
||||
if(!shp->subshare)
|
||||
{
|
||||
if(sp->pwd && access(sp->pwd,X_OK)<0)
|
||||
{
|
||||
free(sp->pwd);
|
||||
sp->pwd = NIL(char*);
|
||||
}
|
||||
if(!sp->pwd)
|
||||
sh_subfork();
|
||||
}
|
||||
#endif /* _lib_fchdir */
|
||||
sh_offstate(SH_INTERACTIVE);
|
||||
sh_exec(t,flags);
|
||||
|
@ -842,32 +848,21 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
|
|||
free((void*)savsig);
|
||||
}
|
||||
shp->options = sp->options;
|
||||
if(shp->pwd != sp->pwd && (!shp->pwd || !sp->pwd || strcmp(sp->pwd,shp->pwd)))
|
||||
{
|
||||
/* restore the present working directory */
|
||||
Namval_t *pwdnod = sh_scoped(shp,PWDNOD);
|
||||
/* restore the present working directory */
|
||||
#if _lib_fchdir
|
||||
if(fchdir(sp->pwdfd) < 0)
|
||||
if(sp->pwdfd > 0 && fchdir(sp->pwdfd) < 0)
|
||||
#else
|
||||
if(!sp->pwd || chdir(sp->pwd) < 0)
|
||||
if(sp->pwd && strcmp(sp->pwd,shp->pwd) && chdir(sp->pwd) < 0)
|
||||
#endif /* _lib_fchdir */
|
||||
{
|
||||
saveerrno = errno;
|
||||
fatalerror = 2;
|
||||
}
|
||||
shp->pwd=sp->pwd;
|
||||
path_newdir(shp,shp->pathlist);
|
||||
if(nv_isattr(pwdnod,NV_NOFREE))
|
||||
pwdnod->nvalue.cp = (const char*)sp->pwd;
|
||||
}
|
||||
else if(sp->shpwd != shp->pwd)
|
||||
{
|
||||
shp->pwd = sp->pwd;
|
||||
if(PWDNOD->nvalue.cp==sp->shpwd)
|
||||
PWDNOD->nvalue.cp = sp->pwd;
|
||||
saveerrno = errno;
|
||||
fatalerror = 2;
|
||||
}
|
||||
else
|
||||
free((void*)sp->pwd);
|
||||
else if(sp->pwd && strcmp(sp->pwd,shp->pwd))
|
||||
path_newdir(shp,shp->pathlist);
|
||||
if(shp->pwd)
|
||||
free((void*)shp->pwd);
|
||||
shp->pwd = sp->pwd;
|
||||
#if _lib_fchdir
|
||||
if(sp->pwdclose)
|
||||
close(sp->pwdfd);
|
||||
|
@ -940,6 +935,8 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
|
|||
UNREACHABLE();
|
||||
case 2:
|
||||
/* reinit PWD as it will be wrong */
|
||||
if(shp->pwd)
|
||||
free((void*)shp->pwd);
|
||||
shp->pwd = NIL(const char*);
|
||||
path_pwd(shp,0);
|
||||
errno = saveerrno;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue