views:

99

answers:

1

Below is a pared-down (error/null checks omitted) snippet of C/Obj-C code that uses sysctl to get the argv of a particular process with PID 50.

...
int getProcessArgs[3] = { CTL_KERN, KERN_PROCARGS, 50 };
sysctl(getProcessArgs, 3, NULL, &length, NULL, 0);
char* processArgs = malloc(length * sizeof(char));
sysctl(getProcessArgs, 3, processArgs, &length, NULL, 0);
...

The first call to sysctl (to determine the size of the argv string array) succeeds. The returned length is ~1600, larger than I would expect, but I suppose not unreasonable. Malloc succeeds. The second call to sysctl returns -1, setting errno to 22, E_INVAL.

I've looked at other code, including that from this question, but can't see the problem with mine. What am I missing?

+2  A: 

I tried wrapping your code up into a program, and it works fine and prints out the other process's argv etc when inquiring about one of my own processes, i.e., one with the same uid as the process invoking sysctl().

The "larger than I would expect" aspect is because the process's environment variables are returned as well as the command line arguments. (It's not obvious what the format of all this information is.)

When inquiring about a different user's process, I get the same EINVAL from the second sysctl that you've been seeing. I guess this is considered unreasonable curiosity about other people's processes, but you'd think the first sysctl would fail too.

(When inquiring about a non-existent pid, the first sysctl fails with EINVAL.)

This all seems to be massively underdocumented: on Leopard, KERN_PROCARGS doesn't even appear in the sysctl man page.

John Marshall
That's what it is; when I try with a self-owned PID, it works. I also did some digging around in http://www.opensource.apple.com/source/xnu/xnu-792.12.6/bsd/kern/kern_sysctl.c (see sysctl_procargsx), and it does in fact check permissions in the actually-getting-args case, but not when you just get the length. Now I just wonder how 'ps' is able to get the args for all processes, but I guess that's for a separate question.
DNS
The usual approach for `ps` is to be setuid root and thus able to use KERN_PROCARGS or other superpowers arbitrarily. That sysctl_procargsx() source code is interesting in that it just returns the base of the user stack -- returning the main(argc,argv,envp) stack frame makes it clear why the environment variables are also returned, and says...er... something about the format of the returned data.
John Marshall