Repository: Gallopsled/pwntools

Issue:
Debugger always exiting. 
### Updating / Verifying on Ubuntu / etc.

I've created a docker container to replicate my issue based off the latest Pwntools, Ubuntu 18.04, etc. Here is the docker file, it does the basic install Python3, install Pwntools, and setup `/etc/sysctl.d/10-ptrace.conf` to be zero. 
```
FROM ubuntu:18.04

ENV LC_CTYPE=C.UTF-8
ENV HOME="/root"
ENV DEBIAN_FRONTEND noninteractive

RUN sed -i 's/http:\/\/archive/http:\/\/au.archive/g' /etc/apt/sources.list
RUN apt update

RUN apt install -y gdb python3.8-dev python3-pip tmux
RUN python3 -m pip install --upgrade pip
RUN python3 -m pip install --upgrade pwntools

RUN sed -i 's/1/0/g' /etc/sysctl.d/10-ptrace.conf
```

Docker is started with the following command. 

`docker run -it --privileged --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -v ${pwd}:/root/scratch pwntest:latest`

### Script / Target

I'm trying to do a basic GDB attach onto a target binary. If it matters, this is a ROP challenge from CSAW 2020. It ([rop.zip](https://github.com/Gallopsled/pwntools/files/5235911/rop.zip)) has been attached to this report to allow replication. 

The debug pwntools script is as follow. 

```
#!/usr/bin/env python3

import pwn

command_line = ['./rop']
arch = 'amd64'

target = pwn.ELF(command_line[0])

gdb_commands = """
break main
"""

env = {}

pwn.context.terminal = ['tmux', 'splitw', '-h', '-p', '60']
pwn.context.update(arch=arch, os='linux')

p = pwn.process(command_line, env=env)
pwn.gdb.attach(p, gdbscript=gdb_commands)

p.sendline("Hello")
p.interactive()
```

### Results / Errors
![image](https://user-images.githubusercontent.com/10124857/93413575-e5356300-f8e2-11ea-9628-b65a74e4e8e0.png)
![image](https://user-images.githubusercontent.com/10124857/93413593-ee263480-f8e2-11ea-8871-ea2327c8dc60.png)

So the issue here is the debugger fails to attach from the perspective of the script. It gives an error or "debugger exited!". This in turn leads to other issues such as de syncing between script / debugger, debugged processes being terminated, etc. 

The error suggests it may be an issues with ptrace_scope. I've made the docker container specifically to disable this / attempt to resolve it. I understand docker requires some additional commands to allow ptrace scope, you can see the command used above. 

### Debug Logs, etc.
I also forgot to run with debug enabled in the screenshots, so I've done it a second time. Results attached in 
[log.txt](https://github.com/Gallopsled/pwntools/files/5235983/log.txt)




Discussion:
Could not reproduce this locally, but the probable solution is: make sure that `tmux splitw` actually works outside pwntools and only then use it as `context.terminal`.
Thanks for your response @Arusekk . I've confirmed `tmux splitw` actually works fine outside of pwntools. You can also see in the first provided screenshot that tmux successfully splits as well. 

When you say you couldn't reproduce this locally, is that with the provided docker container? Or just with the provided script? 
Alright, the current logic in pwntools is wrong. It assumes that the debugged process gets first traced, and then the `tmux splitw...` process exits (which is totally unreliable, and even mostly false). If you wish, you can submit a pull request addressing the issue (a solution I can think of is doing some kind of a startup notification by the command started in the new terminal — or we could just not check debugger status when the `run_in_new_terminal` function does not spawn a persistent process). Either way, the issue you reported is a race condition that can be either avoided or eliminated.
OR one could actually request some `tmux splitw --wait` flag upstream in tmux! (They currently have `-I` (capital i), which could technically be leveraged (link below), but that would be a terrible thing to do)

https://github.com/tmux/tmux/blob/ca5afb34bfdcaddbaed12e7375b48c05019e4c18/cmd-split-window.c#L186-L187
I'm down for using a lock file using `mkdtemp()` or something and creating a file after GDB has started / stopped / attached / whatever/
so guys, any news? i'm having the same issue.
I'm for a temporary fix that does not panic after the debugger exit, but sleeps for an extra time period instead.

Resolving patch:
diff --git a/pwnlib/util/proc.py b/pwnlib/util/proc.py
--- a/pwnlib/util/proc.py
+++ b/pwnlib/util/proc.py
@@ -333,7 +333,7 @@ def wait_for_debugger(pid, debugger_pid=None):
     t = Timeout()
     with t.countdown(timeout=15):
         with log.waitfor('Waiting for debugger') as l:
-            if debugger_pid:
+            while debugger_pid:
                 debugger = psutil.Process(debugger_pid)
                 while t.timeout and tracer(pid) is None:
                     try:
@@ -341,12 +341,15 @@ def wait_for_debugger(pid, debugger_pid=None):
                     except psutil.TimeoutExpired:
                         pass
                     else:
-                        l.failure("debugger exited! (maybe check /proc/sys/kernel/yama/ptrace_scope)")
+                        debugger_pid = 0
+                        break
             else:
                 while t.timeout and tracer(pid) is None:
                     time.sleep(0.01)
 
         if tracer(pid):
             l.success()
+        elif debugger_pid == 0:
+            l.failure("debugger exited! (maybe check /proc/sys/kernel/yama/ptrace_scope)")
         else:
             l.failure('Debugger did not attach to pid %d within 15 seconds', pid)


Test patch: