Persistent mode: stray process left behind

171 views
Skip to first unread message

Jakub Wilk

unread,
Aug 8, 2016, 2:15:13 PM8/8/16
to afl-...@googlegroups.com
Hi Michal!

If you use the persistent mode, afl-fuzz doesn't always kill the target
program on exit:

$ afl-clang-fast -Wall test-instr-persistent.c -o test-instr-persistent
afl-clang-fast 2.28b by <lsze...@google.com>
afl-llvm-pass 2.28b by <lsze...@google.com>
[+] Instrumented 10 locations (hardened mode, ratio 100%).

$ mkdir in out && echo moo > in/moo

$ timeout 1 afl-fuzz -i in -o out -- ./test-instr-persistent
afl-fuzz 2.28b by <lca...@google.com>
...
[+] All set and ready to roll!


+++ Testing aborted by user +++
[+] We're done here. Have a nice day!

$ ps ax | grep '[T] .*test-instr-persistent'
1234 ? T 0:00 ./test-instr-persistent


I believe this is how it happens:

afl-llvm-rt.o.c:192: The target program stops itself with SIGSTOP.

afl-llvm-rt.o.c:156: The forkserver finishes wait()ing and sends the
status to afl-fuzz.

afl-fuzz.c:2395: run_target() reads the status...

afl-fuzz.c:2404: ... and sets child_pid to 0.

SIGTERM arrives.

afl-fuzz.c:2404: handle_stop_sig() kills the fork server, but not the
target program. Oops!


This was originally reported by Daniel Stender:
https://bugs.debian.org/833675

--
Jakub Wilk
test-instr-persistent.c

Michal Zalewski

unread,
Aug 8, 2016, 5:48:13 PM8/8/16
to afl-users
Thanks for the analysis! Does changing this in run_target():

child_pid = 0;

...to:

if (!WIFSTOPPED(&status)) child_pid = 0;

...help? I think it should.
> --
> You received this message because you are subscribed to the Google Groups
> "afl-users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to afl-users+...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Jakub Wilk

unread,
Aug 9, 2016, 7:35:13 AM8/9/16
to afl-...@googlegroups.com
* Michal Zalewski <lca...@gmail.com>, 2016-08-08, 14:47:
>Thanks for the analysis! Does changing this in run_target():
>
> child_pid = 0;
>
>...to:
>
> if (!WIFSTOPPED(&status)) child_pid = 0;
>
>...help? I think it should.

With s/&//, it does seem to fix the problem with the test case I
attached.

But I still see stray stopped processes after running python-afl's test
suite. :-\ I will investigate further.

--
Jakub Wilk

Jakub Wilk

unread,
Aug 10, 2016, 12:41:34 PM8/10/16
to afl-...@googlegroups.com
So SIGTERM could arrive after the forkserver spawned a new child
process, but before it managed to send the pid back to afl-fuzz.

Perhaps the forkserver could create a new process group with setpgid(0,
0); then afl-fuzz could kill the whole group with kill(-forksrv_pid,
SIGKILL).

--
Jakub Wilk

Michal Zalewski

unread,
Aug 10, 2016, 3:39:55 PM8/10/16
to afl-users
Why not =) Will implement.

/mz

Michal Zalewski

unread,
Aug 10, 2016, 11:02:48 PM8/10/16
to afl-users
> So SIGTERM could arrive after the forkserver spawned a new child process,
> but before it managed to send the pid back to afl-fuzz.

On second thoughts... now that we keep child_pid across calls to
run_target and update it only on successful read, should it be
happening? Except for the very unlikely case that Ctrl-C is pressed
just as the forkserver spawned a new persistent process, but for
__AFL_LOOP(1000), the odds of that are very low?

/mz

Jakub Wilk

unread,
Aug 16, 2016, 5:50:41 PM8/16/16
to afl-...@googlegroups.com
* Michal Zalewski <lca...@gmail.com>, 2016-08-10, 20:02:
High number of loop iterations doesn't help if the target process
crashes a lot.

Anyway, I've implemented a work-around in python-afl's testsuite, so
this is no longer a big deal for me.

--
Jakub Wilk

Jakub Wilk

unread,
Jan 7, 2019, 3:16:49 AM1/7/19
to afl-...@googlegroups.com
* Jakub Wilk <jw...@jwilk.net>, 2016-08-10, 18:41:
On a second thought, forkserver and its children are already in a
separate process group, thanks to setsid(), so no setpgid() is
necessary.

--
Jakub Wilk
Reply all
Reply to author
Forward
0 new messages