[comp.sys.sun] just what is PTRACE_POKEUSER good for, anyway?

benson@talcott.harvard.edu (Benson I. Margulies) (04/26/89)

The documentation for ptrace in 4.0 says that PTRACE_POKEUSER can be used
to poke the general registers, floating point status, and some psw bits.
However, the enclosed program(s) demonstrates that the only pokable
locations in user proper are high addresses in the kernel stack area, just
below the PCB. This certainly seems wrong, as u_pcb would appear to be a
much more reasonable field of addresses for poking.

If this is a bug, it is a serious security bug, since the kernel is
probably spoofable by sticking junk into a ptrace child's kernel stack.

In any case the documentation for ptrace isn't really good enough, as it
fails to give enough information to make POKEUSER useful. In particular,
if only some fields of the psw are changable, which ones are they?

Benson I. Margulies, Object Design. Sun 3/60 serial 912F0077, under
contract. 1 NE Executive Park, Burlington, MA 01802.  617-270-9797.
odi!benson@talcott.harvard.edu.

#include <sys/types.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <signal.h>

extern int errno;

/* Survey PTRACE_POKEUSER */

main ()
{
    int cpid;
    int uoffset;

    switch(cpid = fork ())
    {
      case 0: /* child */
	be_childish ();
	break;
      case -1: /* error */
	perror("ptrace_poke_survey");
	exit(1);
    }

   /* The parent */

    /* wait for child to send itself signal */
    wait((union wait *) 0);

    /* now start the fun */

    for(uoffset = 0; uoffset < sizeof(struct user);uoffset += sizeof(int)) {
	errno = 0;
	ptrace(PTRACE_POKEUSER, cpid, (char *)uoffset, 0, 0);
	if(errno == 0) printf("Offset %4d\n", uoffset);
    }

    ptrace(PTRACE_KILL, cpid, 0, 0, 0);
    exit(0);
}

be_childish ()
{
    ptrace(PTRACE_TRACEME, 0, 0, 0, 0);
    kill(getpid(), SIGINT);
}

#include <sys/types.h>
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <signal.h>

main()
{
    struct user u;

#define UO(n) ((char *)&u.n - (char *)&u)

    printf("u_stack:\t%d\n", UO(u_stack));
    printf("u_pcb:\t%d\n", UO(u_pcb));
    printf("u_procp:\t%d\n", UO(u_procp));
    printf("u_ar0:\t%d\n", UO(u_ar0));
    printf("u_comm:\t%d\n", UO(u_comm));
    printf("u_arg:\t%d\n", UO(u_arg));
    printf("u_ap:\t%d\n", UO(u_ap));
    printf("u_qsave:\t%d\n", UO(u_qsave));
    printf("u_r:\t%d\n", UO(u_r));
    printf("u_error:\t%d\n", UO(u_error));
    printf("u_eosys:\t%d\n", UO(u_eosys));
    printf("u_ssave:\t%d\n", UO(u_ssave));
    printf("u_signal:\t%d\n", UO(u_signal));
    printf("u_sigmask:\t%d\n", UO(u_sigmask));
    printf("u_sigonstack:\t%d\n", UO(u_sigonstack));
    printf("u_sigintr:\t%d\n", UO(u_sigintr));
    printf("u_sigreset:\t%d\n", UO(u_sigreset));
    printf("u_oldmask:\t%d\n", UO(u_oldmask));
    printf("u_code:\t%d\n", UO(u_code));
    printf("u_addr:\t%d\n", UO(u_addr));
    printf("u_sigstack:\t%d\n", UO(u_sigstack));
    printf("u_ofileX:\t%d\n", UO(u_ofileX));
    printf("u_pofileX:\t%d\n", UO(u_pofileX));
    printf("u_ofile:\t%d\n", UO(u_ofile));
    printf("u_pofile:\t%d\n", UO(u_pofile));
    printf("u_lastfile:\t%d\n", UO(u_lastfile));
    printf("u_cwd:\t%d\n", UO(u_cwd));
    printf("u_cdir:\t%d\n", UO(u_cdir));
    printf("u_rdir:\t%d\n", UO(u_rdir));
    printf("u_ttyp:\t%d\n", UO(u_ttyp));
    printf("u_ttyd:\t%d\n", UO(u_ttyd));
    printf("u_ttyvp:\t%d\n", UO(u_ttyvp));
    printf("u_cmask:\t%d\n", UO(u_cmask));
    printf("u_ru:\t%d\n", UO(u_ru));
    printf("u_cru:\t%d\n", UO(u_cru));
    printf("u_timer:\t%d\n", UO(u_timer));
    printf("u_XXX:\t%d\n", UO(u_XXX));
    printf("u_start:\t%d\n", UO(u_start));
    printf("u_acflag:\t%d\n", UO(u_acflag));
    printf("u_prof:\t%d\n", UO(u_prof));
    printf("u_rlimit:\t%d\n", UO(u_rlimit));
    printf("u_exdata:\t%d\n", UO(u_exdata));
    printf("u_lofault:\t%d\n", UO(u_lofault));
    exit(0);
}