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

Fix argv rewrite on invoking hashbangless script (rhbz#1047506)

The fixargs() function is invoked when ksh needs to run a script
without a #!/hashbang/path. Instead of letting the kernel invoke a
shell, ksh exfile()s the script itself from sh_main(). In the
forked child, it calls fixargs() to set the argument list in the
environment to the args of the new script, so that 'ps' and
/proc/PID/cmdline show the expected output.

But fixargs() is broken because, on systems other than HP-UX (on
which ksh uses pstat(2)), ksh simply inserts a terminating zero.
The arguments list is not a zero-terminated C string. Unix systems
expect the entire arguments buffer to be zeroed out, otherwise 'ps'
and /proc/*/cmdline will have fragments of previous command lines
in the output.

The Red Hat patch for this bug is:
642af4d6/f/ksh-20120801-argvfix.patch

However, that fix is incomplete because 'command_len' was also
hardcoded to be limited to 64 characters (!), which still gave
invalid 'ps' output if the erased command line was longer.

src/cmd/ksh93/sh/main.c: fixargs():

- Remove CMD_LENGTH macro which was defined as 64.

- Remove code that limited the erasure of the arguments buffer to
  CMD_LENGTH characters. That code also had quite a dodgy strdup()
  call -- it copies arguments to the heap, but they are never freed
  (or even used), so it's a memory leak. Also, none of this is
  ever done if the length is calculated using pstat(2) on HP-UX,
  which is a clear indication that it's unnecessary.
  (I think this code block must have been some experiment they
  forgot to remove. One reason why I think so is that a 64 byte
  arguments limit never made sense, even in the 1980s when they
  wrote ksh on 80-column CRT displays. Another indication of this
  is that fixing it didn't require adding anything; the code to do
  the right thing was already there, it was just being overridden.)

- Zero out the full arguments length as in the Red Hat patch.

src/cmd/ksh93/tests/basic.sh:

- Add test. It's sort of involved because 'ps' is one of the least
  portable commands in practice, in spite of standardisation.
This commit is contained in:
Martijn Dekker 2020-09-25 08:19:43 +02:00
parent 651bbd563e
commit cefe087d23
4 changed files with 42 additions and 17 deletions

View file

@ -49,8 +49,6 @@
# include <nc.h>
#endif /* _hdr_nc */
#define CMD_LENGTH 64
/* These routines are referenced by this module */
static void exfile(Shell_t*, Sfio_t*,int);
static void chkmail(Shell_t *shp, char*);
@ -340,6 +338,7 @@ int sh_main(int ac, char *av[], Shinit_f userinit)
}
else
{
/* beenhere > 0: We're in a forked child, about to execute a script without a hashbang path. */
fdin = shp->infd;
fixargs(shp->st.dolv,1);
}
@ -697,6 +696,13 @@ static void chkmail(Shell_t *shp, char *files)
#if defined(_lib_fork) && !defined(_NEXT_SOURCE)
/*
* fix up command line for ps command
*
* This function is invoked when ksh needs to run a script without a
* #!/hashbang/path. Instead of letting the kernel invoke a shell, ksh
* exfile()s the script itself from sh_main(). In the forked child, it calls
* fixargs() to set the argument list in the environment to the args of the
* new script, so that 'ps' and /proc/PID/cmdline show the expected output.
*
* mode is 0 for initialization
*/
static void fixargs(char **argv, int mode)
@ -727,19 +733,6 @@ static void fixargs(char **argv, int mode)
buff = argv[0];
while(cp = *argv++)
command_len += strlen(cp)+1;
if(environ && *environ==buff+command_len)
{
for(argv=environ; cp = *argv; cp++)
{
if(command_len > CMD_LENGTH)
{
command_len = CMD_LENGTH;
break;
}
*argv++ = strdup(cp);
command_len += strlen(cp)+1;
}
}
command_len -= 1;
return;
}
@ -754,7 +747,8 @@ static void fixargs(char **argv, int mode)
offset += size;
buff[offset++] = ' ';
}
buff[offset-1] = 0;
offset--;
memset(&buff[offset], 0, command_len - offset);
# ifdef PSTAT
un.pst_command = stakptr(0);
pstat(PSTAT_SETCMD,un,0,0,0);