From: torek@horse.ee.lbl.gov (Chris Torek)
Newsgroups: comp.sys.sun.apps,comp.unix.programmer,comp.unix.internals
Subject: Re: modifying ARGV within a running program
Date: 23 Jan 1993 13:18:09 GMT
Organization: Lawrence Berkeley Laboratory, Berkeley CA
NNTP-Posting-Host: 128.3.112.15

In article <1jqninINNa8l@usenet.INS.CWRU.Edu> edvax@pyrite.som.cwru.edu
(Ed Reznichenko) writes:
>Does anyone know how to SAFELY modify [what ps will show for a process]?
>Any ideas on how to do this portably (if at all) would be appreciated.

It cannot be done portably.  In fact, on some UNIX systems, it cannot
be done at all.  These systems save away the first few arguments at
process startup, keeping it in a kernel area off limits to normal user
code.  Here the `ps' command reads this kernel area.  Since the kernel
area is read-only, the stuff ps will show is likewise read-only.

Since this is going to comp.sys.sun.apps: on Suns running SunOS, at
least---I know nothing about the new Solaris systems---the strings ps
prints are found at the top of the user stack.  (This is true in all
traditional BSD-based systems.)  Specifically, on a SPARC, the layout
is:

	 [end of user accessible space]	  0xf8000000 (approx.)
	--------------------------------- 0xf7ffffff (approx.)
	|	   <reserved>		|  < fixed but system-dependent size
	+-------------------------------+
	|     strings & <alignment>	|  < variable size
	+-------------------------------+
	|	      NULL		|	^  higher addresses
	|	 environ[ne - 1]	|	|
	|	 environ[ne - 2]	|	|
	|	       ...		|	|
	|	    environ[0]		|	v  lower addresses
	+-------------------------------+
	|	      NULL		|
	|	 argv[argc - 1]		|
	|	 argv[argc - 2]		|  < each pointer is 4 bytes
	|	       ...		|
	|	     argv[0]		|
	+-------------------------------+
	|	      argc		|  < argc is also 4 bytes
	+-------------------------------+
	|       <register window>	|  < exactly 64 bytes
	+-------------------------------+ <-- %sp at program startup

In SunOS, the <reserved> space is probably empty.  In 4.4BSD on the
SPARC, it contains the signal trampoline code and something I will
get to in a moment.

The strings, plus any needed padding for alignment, appear immediately
below the reserved space.  They are `in order', with the string for
argv[0] at the lowest address and the last environment string at the
highest.  These are immediately preceded by pointers to each string;
these make up the environ[] and argv[] arrays.  The `ps' program scans
backwards (under SunOS, and used to under BSD) through the top few user
pages looking for the argv strings.  Since they are in plain old user
space pages, it is possible to overwrite them.  Note, however, that
overwriting more space than is available will cause trouble.  Since the
environment strings immediately follow the argv strings, it is possible
to clobber one's own environment, causing library routines such as
execlp() and getenv() to fail mysteriously.  (Under BSD, one could even
overwrite the signal trampoline code.)

Incidentally, this is also the reason that a large environment can
make `ps' fail to find arguments.  If those strings take up enough
space, ps's backwards scan fails, as it only reads the last few pages.

In 4.4BSD, the very top of the <reserved> area always contains a
structure known as `ps_strings', as defined in <sys/exec.h>:

/*
 * The following structure is found at the top of the user stack of each
 * user process. The ps program uses it to locate argv and environment
 * strings. Programs that wish ps to display other information may modify
 * it; normally ps_argvstr points to the text for argv[0], and ps_nargvstr
 * is the same as the program's argc. The fields ps_envstr and ps_nenvstr
 * are the equivalent for the environment.
 */
struct ps_strings {
	char	*ps_argvstr;	/* first of 0 or more argument strings */
	int	ps_nargvstr;	/* the number of argument strings */
	char	*ps_envstr;	/* first of 0 or more environment strings */
	int	ps_nenvstr;	/* the number of environment strings */
};

A C library routine (not yet written, but to be called `setproctitle'
---this is the name used in sendmail, ftp, etc) will provide a portable
interface to altering ps_strings.  The `ps' program simply reads the
pointer and count, then fetchs ps_nargvstr NUL-terminated strings
starting at ps_argvstr.  There is no need to arrange `extra' space in
advance for long setproctitle()s, as the space can simply be allocated
on the heap.

Note: all of the above is based on the alpha 4.4BSD SPARC kernel.  While
this kernel runs SunOS binaries, there is no guarantee that the SunOS
C startup code does not modify the picture somehow.
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 510 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov