Repository: pwndbg/pwndbg

Issue:
nextcall with symbol bug
### Description

after nextcall with unknown symbol/address (like nextcall lol) gdb won't run again

### Steps to reproduce
```
gdb whatever
> start
> nextcall lol
> start
> continue
Warning:
Cannot insert breakpoint -46.
Cannot access memory at address 0x7ffff7a6f916

Command aborted.
```

### My setup

GNU gdb (Debian 7.12-6) 7.12.0.20161007
nextcall with symbol bug
### Description

after nextcall with unknown symbol/address (like nextcall lol) gdb won't run again

### Steps to reproduce
```
gdb whatever
> start
> nextcall lol
> start
> continue
Warning:
Cannot insert breakpoint -46.
Cannot access memory at address 0x7ffff7a6f916

Command aborted.
```

### My setup

GNU gdb (Debian 7.12-6) 7.12.0.20161007


Discussion:
TODO:
* we need better docs what does the argument for `nextcall`, `stepcall`, etc. do
* this needs to be fixed - maybe we should check if given argument is a valid address or a symbol that resolves to valid address?

By valid address I mean `a mapped address`.

@GrosQuildu Are you going to work on that, so I can assign you to the task?
This is an issue with symbol resolution in dynamically loaded libraries --
the address can't be known ahead of time, and you can't know if the symbol
will exist at all until runtime.

Take a look at "set breakpoint pending" and the "Breakpoint.pending"
attribute.

All that said, none of that should matter with "nextcall" since we stop on
EVERY call instruction (in the current frame) and check for a regex.

*Zach Riggle*


On Fri, Sep 21, 2018 at 8:56 AM Disconnect3d <notifications@github.com>
wrote:

> TODO:
>
>    - we need better docs what does the argument for nextcall, stepcall,
>    etc. do
>    - this needs to be fixed - maybe we should check if given argument is
>    a valid address or a symbol that resolves to valid address?
>
> By valid address I mean a mapped address.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/pwndbg/pwndbg/issues/532#issuecomment-423539555>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AAG0GGmm7MV7gujgnLz3MnhRi73G38Mcks5udPAkgaJpZM4W0FvE>
> .
>

Agreed, can't do forward checks.
The problem seems to be related to temporary breakpoints (setted at pwndbg/next.py:75). Changing internal arg to False reveals that the last temporary breakpoint is never deleted (and is in libc). After manual deletion program can be started again.

But internal breakpoints have negative numbering (check with `maint info breakpoints`) and seems to be irremovable :D Noidea how to fix that
I looked into this a bit more.
The cleanest way I could think of to fix this was to have some function that clears all temporary internal breakpoints on program termination. 

The first thing I thought of was to listen for the exit event. However it seems that gdb doesn't necessarily send the exited event even if a process is terminating. 

The only event that seemed to be reliably sent after program termination was the `before_prompt` event. 

I came up with the following function to do that:
```
@pwndbg.events.exit
@pwndbg.events.before_prompt
def clear_temp_breaks():
    if not pwndbg.proc.alive:
        breakpoints = gdb.breakpoints()
        if breakpoints:
            for bp in breakpoints:
                if bp.temporary and not bp.visible: #visible is used instead of internal because older gdb's don't support internal 
                    bp.delete()
```
Before opening a pr, I had one question.
1) Is there any chance that removing all temporary internal breakpoints could break future functionality? I don't think anything besides the next* functions currently use them, but something might use them in the future.
> Is there any chance that removing all temporary internal breakpoints could break future functionality?

I can't think of a feature that would use temporary internal breakpoints between program launches, so I think we are fine with your proposed solution :).

> However it seems that gdb doesn't necessarily send the exited event even if a process is terminating.

Heh, if that is true, that is sad :'(
Could we check that without pwndbg and report this to GDB?

> > However it seems that gdb doesn't necessarily send the exited event even if a process is terminating.
> 
> Heh, if that is true, that is sad :'(
> Could we check that without pwndbg and report this to GDB?

Just looked at this a bit more. Looks like I was wrong about the exited event not being sent. I was confused because I `set debug-events on` but there was no indication of a exited event being shown. It turns out this is because we do `gdb.execute('continue', from_tty=False, to_string=True)`, which results in the debug information being captured by the `to_string=True`.
This might be worth fixing for the future if possible? What do you think @disconnect3d?

This to_string thing also results in it not necessarily being clear when the program exits. For an example of that, see the following output.
```
user@ubuntu:~/Desktop/gdb/pie$ gdb ./pie 
pwndbg: loaded 179 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./pie...(no debugging symbols found)...done.
pwndbg> set context-output /dev/pts/5
Set where pwndbg should output ("stdout" or file/tty). to '/dev/pts/5'
pwndbg> start
Temporary breakpoint 1 at 0x555555554620

Temporary breakpoint 1, 0x0000555555554620 in _start ()
pwndbg> nextcall whatever
in func
pwndbg> c
The program is not being run.
```
Normally you would have something like `[Inferior 1 (process 48827) exited with code 02]`. Fixing this would be a totally separate issue, and I'm not sure if it's even worth it. Just figured I would bring it up now.
TODO:
* we need better docs what does the argument for `nextcall`, `stepcall`, etc. do
* this needs to be fixed - maybe we should check if given argument is a valid address or a symbol that resolves to valid address?

By valid address I mean `a mapped address`.

@GrosQuildu Are you going to work on that, so I can assign you to the task?
This is an issue with symbol resolution in dynamically loaded libraries --
the address can't be known ahead of time, and you can't know if the symbol
will exist at all until runtime.

Take a look at "set breakpoint pending" and the "Breakpoint.pending"
attribute.

All that said, none of that should matter with "nextcall" since we stop on
EVERY call instruction (in the current frame) and check for a regex.

*Zach Riggle*


On Fri, Sep 21, 2018 at 8:56 AM Disconnect3d <notifications@github.com>
wrote:

> TODO:
>
>    - we need better docs what does the argument for nextcall, stepcall,
>    etc. do
>    - this needs to be fixed - maybe we should check if given argument is
>    a valid address or a symbol that resolves to valid address?
>
> By valid address I mean a mapped address.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/pwndbg/pwndbg/issues/532#issuecomment-423539555>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AAG0GGmm7MV7gujgnLz3MnhRi73G38Mcks5udPAkgaJpZM4W0FvE>
> .
>

Agreed, can't do forward checks.
The problem seems to be related to temporary breakpoints (setted at pwndbg/next.py:75). Changing internal arg to False reveals that the last temporary breakpoint is never deleted (and is in libc). After manual deletion program can be started again.

But internal breakpoints have negative numbering (check with `maint info breakpoints`) and seems to be irremovable :D Noidea how to fix that
I looked into this a bit more.
The cleanest way I could think of to fix this was to have some function that clears all temporary internal breakpoints on program termination. 

The first thing I thought of was to listen for the exit event. However it seems that gdb doesn't necessarily send the exited event even if a process is terminating. 

The only event that seemed to be reliably sent after program termination was the `before_prompt` event. 

I came up with the following function to do that:
```
@pwndbg.events.exit
@pwndbg.events.before_prompt
def clear_temp_breaks():
    if not pwndbg.proc.alive:
        breakpoints = gdb.breakpoints()
        if breakpoints:
            for bp in breakpoints:
                if bp.temporary and not bp.visible: #visible is used instead of internal because older gdb's don't support internal 
                    bp.delete()
```
Before opening a pr, I had one question.
1) Is there any chance that removing all temporary internal breakpoints could break future functionality? I don't think anything besides the next* functions currently use them, but something might use them in the future.
> Is there any chance that removing all temporary internal breakpoints could break future functionality?

I can't think of a feature that would use temporary internal breakpoints between program launches, so I think we are fine with your proposed solution :).

> However it seems that gdb doesn't necessarily send the exited event even if a process is terminating.

Heh, if that is true, that is sad :'(
Could we check that without pwndbg and report this to GDB?

> > However it seems that gdb doesn't necessarily send the exited event even if a process is terminating.
> 
> Heh, if that is true, that is sad :'(
> Could we check that without pwndbg and report this to GDB?

Just looked at this a bit more. Looks like I was wrong about the exited event not being sent. I was confused because I `set debug-events on` but there was no indication of a exited event being shown. It turns out this is because we do `gdb.execute('continue', from_tty=False, to_string=True)`, which results in the debug information being captured by the `to_string=True`.
This might be worth fixing for the future if possible? What do you think @disconnect3d?

This to_string thing also results in it not necessarily being clear when the program exits. For an example of that, see the following output.
```
user@ubuntu:~/Desktop/gdb/pie$ gdb ./pie 
pwndbg: loaded 179 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./pie...(no debugging symbols found)...done.
pwndbg> set context-output /dev/pts/5
Set where pwndbg should output ("stdout" or file/tty). to '/dev/pts/5'
pwndbg> start
Temporary breakpoint 1 at 0x555555554620

Temporary breakpoint 1, 0x0000555555554620 in _start ()
pwndbg> nextcall whatever
in func
pwndbg> c
The program is not being run.
```
Normally you would have something like `[Inferior 1 (process 48827) exited with code 02]`. Fixing this would be a totally separate issue, and I'm not sure if it's even worth it. Just figured I would bring it up now.

Resolving patch:
diff --git a/pwndbg/next.py b/pwndbg/next.py
--- a/pwndbg/next.py
+++ b/pwndbg/next.py
@@ -27,6 +27,14 @@
 
 interrupts = set((capstone.CS_GRP_INT,))
 
+@pwndbg.events.exit
+def clear_temp_breaks():
+    if not pwndbg.proc.alive:
+        breakpoints = gdb.breakpoints()
+        if breakpoints:
+            for bp in breakpoints:
+                if bp.temporary and not bp.visible: #visible is used instead of internal because older gdb's don't support internal 
+                    bp.delete()
 
 def next_int(address=None):
     """


Test patch: