codeblog code is freedom — patching my itch

February 25, 2010

fun with process scheduling

Filed under: Debian,Ubuntu,Ubuntu-Server — kees @ 11:04 am

In an attempt to force an exec to totally stall so I could attach gdb to a process that does a setuid transition without freaking it out (i.e. can’t launch “sudo gdb …”), I briefly played with process priority, scheduling, and CPU affinity (pinning a process to a CPU).

So far, the best attempt at stalling the process was to set CPU affinity, set its scheduling policy (IDLE), drop the priority value fully (19), and then run a CPU hog with the same CPU affinity with a very high priority (-20).

Staller: sudo schedtool -a 0 -N -n -20 -e /tmp/spin-cpu
Stallee: schedtool -a 0 -D -n 19 -e /usr/bin/sudo -s

The hope was to then run “sudo gdb /usr/bin/sudo $(pidof sudo)“, but it seems that this still isn’t enough to make the exec of sudo totally stall while the “spin-cpu” process is running — it certainly executes more slowly, but I want it to stop.

I have this feeling like I’m missing something obvious about how to accomplish this gdb session sanely. I wonder if I can get gdb to switch targets across a child exec, i.e. run gdb as root on a program that fully drops privs, but then execs sudo. Guess it’s time to go read the gdb manual some more…

© 2010, Kees Cook. This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 License.
CC BY-SA 4.0

10 Comments

  1. What’s about kill -STOP and kill -CONT?

    Comment by Markus Hochholdinger — February 25, 2010 @ 11:42 am

  2. Hmm, yeah, might work, though I think it would still be a hard race — I’d need to have something spinning with a “sudo kill -STOP $(pidof sudo)”.

    Comment by kees — February 25, 2010 @ 11:59 am

  3. With ptrace(2), you can have a child process stop upon calling exec(2). Essentially, it starts up in the stopped state. Perhaps you can use that somehow?

    Comment by Branden — February 25, 2010 @ 1:28 pm

  4. Maybe you could compile a custom version of sudo that runs kill(getpid(),SIGSTOP) on itself as the first thing it does, thereby giving you time all the time in the world to attach to the process and run kill -CONT when you’re ready.

    Comment by Ken Bloom — February 25, 2010 @ 4:58 pm

  5. Yeah, a monster sleep or a SIGSTOP-self works, but I was hoping to do this without changing the target binary. As for ptrace, yes, that’s what gdb uses for debugging. The problem is that I cannot directly use ptrace on a setuid process. I want to examine the transition from unprivileged to privileged, and I can only do that if my debugger process has the caps for it (i.e. running as root). Which means I also can’t directly spawn it.

    Comment by kees — February 25, 2010 @ 5:04 pm

  6. If you want to modify the behavior of the process to study without modifying the binary, you may be able to do this by having it use a modified .so through LD_PRELOAD or LD_LIBRARY_PATH.

    Why not try writing a small lib that does just kill(getpid(),SIGSTOP) and loading it through LD_PRELOAD… ?

    A little googling on “LD_PRELOAD setuid” seem to show that some checks are done by ld.so to ignore LD_PRELOAD for setuid binaries in most cases, but that it is still doable if the lib you use is installed in the right path with the right flags.

    http://lists.netisland.net/archives/plug/plug-2006-05/msg00018.html

    So, you’re likely to need to play a little bit to get it to work, but it would seem doable that way.

    Comment by Thomas — February 26, 2010 @ 4:55 am

  7. So don’t directly spawn it, but rather spawn a shim process that drops priv’s before then invoking the setuid process. The debugger should be able to follow the process tree, remaining as root.

    Comment by Branden — February 26, 2010 @ 11:06 am

  8. @Thomas hunh, cool. I didn’t realize that ld.so did such careful filtering. I thought it just dropped LD_PRELOAD on the ground. Yeah, that works very nicely — jamming a sleep() in the init section of a setuid lib in /lib should work great.

    @Branden I wasn’t sure if gdb would play nicely across the exec (i.e. loading the new ELF image).

    Comment by kees — February 26, 2010 @ 2:33 pm

  9. Looks like I’d need ‘set follow-fork-mode child’ ‘catch exec’ ‘catch fork’ in gdb, or this in a setuid /lib .so:
    int _init(void) {
    int seconds = 0;
    char *str = getenv(“SLEEP”);
    if (str) seconds = atoi(str);
    sleep(seconds);
    return 0;
    }

    Comment by kees — February 26, 2010 @ 3:04 pm

  10. if you can get gdb to follow children, you might as well just do a sh -c ‘sleep 60; sudo …’ and attach to the sh process

    Comment by Patrik — February 27, 2010 @ 2:07 pm

Powered by WordPress