Peter Eriksson
2005-Jun-29 12:00 UTC
[dtrace-discuss] Accessing environment variables in a DTrace script?
I''ve been reading the DTrace documentation, but I can''t find any easy way to access a process environment variables from within a DTrace script? I''m sure it can be done, but how? This message posted from opensolaris.org
Michael Shapiro
2005-Jun-29 22:01 UTC
[dtrace-discuss] Accessing environment variables in a DTrace script?
> I''ve been reading the DTrace documentation, but I can''t find any easy way to > access a process environment variables from within a DTrace script? I''m sure > it can be done, but how?This is a bit tricky because (a) we don''t yet have a supported stable interface for that part of the process model and (b) you fundamentally have an array of varying size you want to examine but you can''t just write a for loop in D to get the environment set at the time you start instrumenting. I''m going to file an RFE for you for us to add a stable interface for this. In the meantime here is one approach you can use: 1. Start a new shell and get its pid by doing echo $$ or whatever. I''m going to assume that we want to get the environment of some program that you can start up, for which I''ll use xclock as an example. $ ksh $ echo $$ 123456 2. Start up dtrace on the supplied "env.d" script, quoted below, using this PID as the argument, like this: $ dtrace -q -s env.d 123456 At this point DTrace will hang out waiting for an exec or a putenv(3C) by the specified PID. Then go back to your shell and run whatever you want: $ exec xclock 3. At this point, given my sample script, you''ll see it print out env[n] = v for everything initially in the environment and every putenv() thereafter. I just have it print these out. If you want to access a particular variable after the process starts from some other probe, you can then access the env[] associative array whenever you like. So, just for kicks, imagine you wanted to trace $PWD at each write(2), you could add this to my script: syscall::write:entry /pid == $1 && env["PWD"] != 0/ { printf("%s\n", env["PWD"]); } Meantime, we''ll try to make this easier in the future. Note that my script requires Solaris Express snv_16 or later or OpenSolaris since it makes use of the new string subroutines that Bryan added in snv_15. Also note that the number "1024" below is a bit of a hack -- if you have a giant envp[] array passed to exec just crank that number up a bit. -Mike -- Mike Shapiro, Solaris Kernel Development. blogs.sun.com/mws/ /* * Script to cache environment variables for pid $1 in env[] * global associative array. */ fbt::stk_copyin:entry /pid == $1 && self->envp == NULL/ { self->envp = (uintptr_t)args[0]->envp; } fbt::stk_copyin:return /self->envp != NULL/ { self->envp = NULL; } fbt::stk_getptr:entry /self->envp != NULL && (uintptr_t)args[1] - self->envp < 1024/ { self->envsp = args[2]; } fbt::stk_getptr:return /self->envsp != NULL && args[1] != 0/ { self->envsp = NULL; } fbt::stk_add:entry /self->envsp != NULL && *self->envsp != NULL/ { this->s = copyinstr((uintptr_t)*self->envsp); this->n = strtok(this->s, "="); this->v = substr(this->s, index(this->s, "=") + 1); printf("env[%s] = %s\n", this->n, this->v); env[this->n] = this->v; self->envsp = NULL; } fbt::stk_add:entry /self->envsp != NULL && *self->envsp == NULL/ { self->envsp = NULL; } pid$1:libc.so.1:putenv:entry { this->s = copyinstr(arg0); this->n = strtok(this->s, "="); this->v = substr(this->s, index(this->s, "=") + 1); printf("env[%s] = %s\n", this->n, this->v); env[this->n] = this->v; }
Ravi Swaminathan
2005-Jun-30 00:48 UTC
[dtrace-discuss] Accessing environment variables in a DTrace script?
Hi Mike, Will I be able to have a two dimensional array point to the env . eg char **env; int i; env=(char **)curpsinfo->pr_envp; i=0; and for loop profile:::tick-1sec / env[i] / { printf("%s\n",env[i]); i++; } Is this expected to work.? Thanks Ravi Michael Shapiro wrote:>>I''ve been reading the DTrace documentation, but I can''t find any easy way to >>access a process environment variables from within a DTrace script? I''m sure >>it can be done, but how? >> >> > >This is a bit tricky because (a) we don''t yet have a supported stable interface >for that part of the process model and (b) you fundamentally have an array of >varying size you want to examine but you can''t just write a for loop in D to >get the environment set at the time you start instrumenting. I''m going to >file an RFE for you for us to add a stable interface for this. In the meantime >here is one approach you can use: > >1. Start a new shell and get its pid by doing echo $$ or whatever. I''m going > to assume that we want to get the environment of some program that you can > start up, for which I''ll use xclock as an example. > > $ ksh > $ echo $$ > 123456 > >2. Start up dtrace on the supplied "env.d" script, quoted below, using this > PID as the argument, like this: > > $ dtrace -q -s env.d 123456 > > At this point DTrace will hang out waiting for an exec or a putenv(3C) > by the specified PID. Then go back to your shell and run whatever you want: > > $ exec xclock > >3. At this point, given my sample script, you''ll see it print out env[n] = v > for everything initially in the environment and every putenv() thereafter. > I just have it print these out. If you want to access a particular > variable after the process starts from some other probe, you can then > access the env[] associative array whenever you like. So, just for kicks, > imagine you wanted to trace $PWD at each write(2), you could add this > to my script: > > syscall::write:entry > /pid == $1 && env["PWD"] != 0/ > { > printf("%s\n", env["PWD"]); > } > >Meantime, we''ll try to make this easier in the future. Note that my >script requires Solaris Express snv_16 or later or OpenSolaris since >it makes use of the new string subroutines that Bryan added in snv_15. >Also note that the number "1024" below is a bit of a hack -- if you >have a giant envp[] array passed to exec just crank that number up a bit. > >-Mike > > >
Peter Eriksson
2005-Jun-30 08:00 UTC
[dtrace-discuss] Re: Accessing environment variables in a DTrace script?
> This is a bit tricky because (a) we don''t yet have a supported stable interface > for that part of the process model and (b) you fundamentally have an array of > varying size you want to examine but you can''t just write a for loop in D to > get the environment set at the time you start instrumenting. I''m going to > file an RFE for you for us to add a stable interface for this. In the meantime > here is one approach you can use:Ah. That''s a pity - it would have been a nice hack to use to prevent users from exploiting the latest local-root hole in Solaris (LD_AUDIT/ld.so) - just run a DTrace script that looks for setuid execution and LD_AUDIT set - if so send that process a SIGKILL and log the attempt :-) This message posted from opensolaris.org
Michael Shapiro
2005-Jun-30 18:39 UTC
[dtrace-discuss] Accessing environment variables in a DTrace script?
> Hi Mike, > > Will I be able to have a two dimensional array point to the env . > > eg > > char **env; > int i; > env=(char **)curpsinfo->pr_envp; > i=0; > and for loop > profile:::tick-1sec > / env[i] / > { > printf("%s\n",env[i]); > i++; > } > > Is this expected to work.?I wasn''t sure how you wanted to access the array, so in my example I made it be an associative array indexed by the name of the environment variable. If you want to simply dump everything you can easily modify my code to use an integer index instead, or keep multiple arrays, one indexed by integer corresponding to _environ[] and other where you convert name to integer index. -Mike -- Mike Shapiro, Solaris Kernel Development. blogs.sun.com/mws/
Nicolas Williams
2005-Jul-01 00:13 UTC
[dtrace-discuss] Re: Accessing environment variables in a DTrace script?
On Thu Jun 30 2005 - 01:00:04 PDT, Peter Eriksson wrote:> Ah. That''s a pity - it would have been a nice hack to use to prevent > users from exploiting the latest local-root hole in Solaris > (LD_AUDIT/ld.so) - just run a DTrace script that looks for setuid > execution and LD_AUDIT set - if so send that process a SIGKILL and log > the attempt :-) This message posted from opensolaris.orgThere is a way. I wrote a pair of D scripts that do it, but at the cost of a system() action per-suid exec(), then Jonathan wrote a much better, one-script replacement: #!/usr/sbin/dtrace -s #pragma D option destructive fbt::stk_add:entry / args[2] == UIO_USERSPACE && ((this->var = strtok(copyinstr(arg1), "=")) == "LD_AUDIT" || this->var == "LD_AUDIT_32" || this->var == "LD_AUDIT_64") / { copyout("X", arg1, 1); } This wouldn''t be stable, but it shows that DTrace can plug that hole. Nico --
Jonathan Adams
2005-Jul-01 01:50 UTC
[dtrace-discuss] Re: Accessing environment variables in a DTrace script?
On 6/30/05, Nicolas Williams <Nicolas.Williams at sun.com> wrote:> On Thu Jun 30 2005 - 01:00:04 PDT, Peter Eriksson wrote: > > Ah. That''s a pity - it would have been a nice hack to use to prevent > > users from exploiting the latest local-root hole in Solaris > > (LD_AUDIT/ld.so) - just run a DTrace script that looks for setuid > > execution and LD_AUDIT set - if so send that process a SIGKILL and log > > the attempt :-) This message posted from opensolaris.org > > There is a way. > > I wrote a pair of D scripts that do it, but at the cost of a system() > action per-suid exec(), then Jonathan wrote a much better, one-script > replacement: > > #!/usr/sbin/dtrace -s > > #pragma D option destructive > > fbt::stk_add:entry > / > args[2] == UIO_USERSPACE && > ((this->var = strtok(copyinstr(arg1), "=")) == "LD_AUDIT" || > this->var == "LD_AUDIT_32" || > this->var == "LD_AUDIT_64") > / > { > copyout("X", arg1, 1); > } > > This wouldn''t be stable, but it shows that DTrace can plug that hole.This shortened version isn''t reliable; you need a to make sure the copyin()s and out()s succeed. This simple version can also munch command line arguments, which may not be what you want. For that matter, this version only works on recent Nevada bits. Cheers, - jonathan
Ravi Swaminathan
2005-Jul-01 02:07 UTC
[dtrace-discuss] Accessing environment variables in a DTrace script?
Hi Mike, This is very useful to know. Peter, Thanks for raising this. Cheers Ravi Michael Shapiro wrote:>>Hi Mike, >> >>Will I be able to have a two dimensional array point to the env . >> >>eg >> >>char **env; >>int i; >>env=(char **)curpsinfo->pr_envp; >>i=0; >>and for loop >>profile:::tick-1sec >>/ env[i] / >>{ >>printf("%s\n",env[i]); >>i++; >>} >> >>Is this expected to work.? >> >> > >I wasn''t sure how you wanted to access the array, so in my example >I made it be an associative array indexed by the name of the >environment variable. If you want to simply dump everything you >can easily modify my code to use an integer index instead, or keep >multiple arrays, one indexed by integer corresponding to _environ[] >and other where you convert name to integer index. > >-Mike > > >
Nicolas Williams
2005-Jul-01 03:21 UTC
[dtrace-discuss] Re: Accessing environment variables in a DTrace script?
On Thu, Jun 30, 2005 at 06:50:32PM -0700, Jonathan Adams wrote:> On 6/30/05, Nicolas Williams <Nicolas.Williams at sun.com> wrote: > > On Thu Jun 30 2005 - 01:00:04 PDT, Peter Eriksson wrote: > > > Ah. That''s a pity - it would have been a nice hack to use to prevent > > > users from exploiting the latest local-root hole in Solaris > > > (LD_AUDIT/ld.so) - just run a DTrace script that looks for setuid > > > execution and LD_AUDIT set - if so send that process a SIGKILL and log > > > the attempt :-) This message posted from opensolaris.org > > > > There is a way. > > > > I wrote a pair of D scripts that do it, but at the cost of a system() > > action per-suid exec(), then Jonathan wrote a much better, one-script > > replacement: > > > > #!/usr/sbin/dtrace -s > > > > #pragma D option destructive > > > > fbt::stk_add:entry > > / > > args[2] == UIO_USERSPACE && > > ((this->var = strtok(copyinstr(arg1), "=")) == "LD_AUDIT" || > > this->var == "LD_AUDIT_32" || > > this->var == "LD_AUDIT_64") > > / > > { > > copyout("X", arg1, 1); > > } > > > > This wouldn''t be stable, but it shows that DTrace can plug that hole. > > This shortened version isn''t reliable; you need a to make sure the > copyin()s and > out()s succeed. This simple version can also munch command line > arguments, which may not be what you want. > > For that matter, this version only works on recent Nevada bits.In that case, consider the attached two scripts, which do work on S10, reliably, from what I can tell, but which slow down start-up of set-id programs. Start the first with the location of the second as its only argument. Nico -- -------------- next part -------------- #!/usr/sbin/dtrace -wFs inline unsigned int SUGID = 0x00002000; inline unsigned int SNOCD = 0x10000000; inline unsigned int PSUIDFLAGS = SUGID|SNOCD; proc:::exec-success /(curthread->t_procp->p_flag & PSUIDFLAGS) != 0/ { stop(); printf("Checking %d (%s)...\n", pid, execname); system("%s %d", $1, pid); } -------------- next part -------------- #!/usr/sbin/dtrace -wFs pid$1:ld.so.1:ld_generic_env:entry / *(char*)copyin(arg0, 1) == ''A''/ { printf("ld_generic_env: GOTCHA!\n"); copyout("X", arg0, 1); printf("ld_generic_env: LD_%c\n", *(char*)copyin(arg0, 1)); } pid$1:a.out:main:entry { exit(0); } BEGIN { system("/bin/prun %d", $1); }