Repository: Gallopsled/pwntools

Issue:
ROP class memory usage with add esp gadget
Creating a `ROP` class with a gadget that adds a large value to the `esp`/`rsp` register causes high memory usage.

The following proof of concept triggers the bug for me on pwntools 4.10.0:

```py
from pwn import *
context.arch = "amd64"
elf = ELF.from_assembly("add esp, 0x12341234; ret")
ROP(elf)  # will hang for a long time and consume a lot of memory
```

The culprit is the following code.
https://github.com/Gallopsled/pwntools/blob/9dfc4d7ee4f6d445526529f885ea1e5c025672fc/pwnlib/rop/rop.py#L1393-L1398 

It decrements the constant added to the stack pointer register in steps of `context.bytes` and adds every intermediate value to a list. This takes a lot of time and consumes a lot of memory.


Discussion:
I can't tell why this list is even being made to begin with. Because all that's going to do is make a list like address, address - 8, address - 16, and so on. Later, this gets saved as a Gadget like so:
https://github.com/Gallopsled/pwntools/blob/e4d3c82501c03de44458ae498a830fe66594f66d/pwnlib/rop/rop.py#L1412
Bug Gadget looks like it's expecting a dict, not a list.
https://github.com/Gallopsled/pwntools/blob/e4d3c82501c03de44458ae498a830fe66594f66d/pwnlib/rop/gadgets.py#L18-L32
And from what I can see, it's expecting to be used like one, like in this search function.
https://github.com/Gallopsled/pwntools/blob/e4d3c82501c03de44458ae498a830fe66594f66d/pwnlib/rop/rop.py#L1426-L1441
Furthermore, the problematic code was introduced in an unrelated pull request. 

Resolving patch:
diff --git a/pwnlib/rop/rop.py b/pwnlib/rop/rop.py
--- a/pwnlib/rop/rop.py
+++ b/pwnlib/rop/rop.py
@@ -717,15 +717,20 @@ def setRegisters(self, registers):
             name = ",".join(goodregs)
             stack.append((gadget.address, gadget))
             for r in gadget.regs:
-                moved += context.bytes
-                if r in registers:
-                    stack.append((registers[r], r))
-                else:
-                    stack.append((Padding('<pad %s>' % r), r))
+                if isinstance(r, str):
+                    if r in registers:
+                        stack.append((registers[r], r))
+                    else:
+                        stack.append((Padding('<pad %s>' % r), r))
+                    moved += context.bytes
+                    continue
+
+                for slot in range(moved, moved + r, context.bytes):
+                    left = gadget.move - slot
+                    stack.append((Padding('<pad %#x>' % left), 'stack padding'))
+                    moved += context.bytes
 
-            for slot in range(moved, gadget.move, context.bytes):
-                left = gadget.move - slot
-                stack.append((Padding('<pad %#x>' % left), 'stack padding'))
+            assert moved == gadget.move
 
         return stack
 
@@ -1389,9 +1394,7 @@ def __getattr__(self, k):
                 elif add.match(insn):
                     arg = int(add.match(insn).group(1), 16)
                     sp_move += arg
-                    while arg >= context.bytes:
-                        regs.append(hex(arg))
-                        arg -= context.bytes
+                    regs.append(arg)
                 elif ret.match(insn):
                     sp_move += context.bytes
                 elif leave.match(insn):


Test patch: