MatthieuDartiailh / bytecode

Python module to modify bytecode
https://bytecode.readthedocs.io/
MIT License
302 stars 38 forks source link

Can't call function with no arguments (py312) #139

Closed 0x32767 closed 11 months ago

0x32767 commented 11 months ago

I wan't to call a function with no arguments, e.g. input(), but I keep getting a RuntimeError saying the stack size is negative. These are the instructions that I have used:

<LOAD_NAME arg='input' location=None>
<CALL arg=0 location=None>
<POP_TOP location=None>
<LOAD_CONST arg=1 location=None>
<RETURN_CONST arg=1 location=None>

Causes:

  File "\venv\Lib\site-packages\bytecode\bytecode.py", line 306, in to_code
    stacksize = cfg.compute_stacksize(
                ^^^^^^^^^^^^^^^^^^^^^^
  File "\venv\Lib\site-packages\bytecode\cfg.py", line 547, in compute_stacksize
    args = coro.send(None)  # type: ignore
           ^^^^^^^^^^^^^^^
  File "\venv\Lib\site-packages\bytecode\cfg.py", line 381, in run
    self._update_size(*effect)
  File "\venv\Lib\site-packages\bytecode\cfg.py", line 419, in _update_size
    size, maxsize, minsize = _update_size(
                             ^^^^^^^^^^^^^
  File "\venv\Lib\site-packages\bytecode\cfg.py", line 159, in _update_size
    raise RuntimeError(msg)
RuntimeError: Failed to compute stacksize, got negative size

However when I call the function with an argument I get the expected output, with the bytecode:

<LOAD_NAME arg='input' location=None>
<LOAD_CONST arg='Some argument' location=None>
<CALL arg=0 location=None>
<POP_TOP location=None>
<LOAD_CONST arg=1 location=None>
<RETURN_CONST arg=1 location=None>

I also looked at the dis module to try and find out how python 3.12 does function calls with no arguments.

  1           0 RESUME                   0

  2           2 LOAD_GLOBAL              1 (NULL + input) # < with no arguments
             12 CALL                     0
             20 POP_TOP

  3          22 LOAD_GLOBAL              1 (NULL + input) # < with arguments
             32 LOAD_CONST               1 ('ok')
             34 CALL                     1
             42 POP_TOP
             44 RETURN_CONST             0 (None)

How could I call input with no arguments?

P403n1x87 commented 11 months ago

You probably need a RESUME at the top, but more importantly you need to push NULL to the stack if you don't want to use LOAD_GLOBAL with the flag set. For example

resume 0
push_null
load_name $input
call 0
pop_top
return_const None

(I'm using spasmlang for simplicity) should work as expected with 3.12

0x32767 commented 11 months ago

Thank you for helping, that worked for me.