Skip to content

PyArmored code crashes stochastically on hash calculation for frame.f_code #2300

@himkt

Description

@himkt

I prepared the simple Python script to get a frame object and try to calculate a hash of f_core.

import sys
from pathlib import Path

# note; pyarmor_segv is simple python module only with pathlib to raise exception.
# https://github.com/himkt/pyarmor-hash-segv/blob/main/src/pyarmor_segv/__init__.py
from pyarmor_segv import func  # pyright: ignore[reportMissingImports]

try:
    func(Path('empty'))
except ValueError:
    # Get the obfuscated function's frame
    tb = sys.exc_info()[2].tb_next  # type: ignore
    frame = tb.tb_frame  # type: ignore
    code = frame.f_code
    print(f"frame.f_code: {code}")
    print(f"co_name: {code.co_name}")
    print(f"Computing hash(frame.f_code)...: {hash(code)}")
    print("Success")

I repeatedly generated the obfuscated module and ran the script (replicate.sh, you can see the script in https://github.com/himkt/pyarmor-hash-segv) to get segmentation faults stochastically.

> ./replicate.sh 
Trial  1: frame.f_code: <code object func at 0x7776cf78c370, file "<frozen pyarmor_segv>", line 5>
co_name: func
Computing hash(frame.f_code)...: -3915728441605713561
Success
Normal ✓
Trial  2: frame.f_code: <code object func at 0x7251b5ec8370, file "<frozen pyarmor_segv>", line 5>
co_name: func
SEGFAULT ✗
Trial  3: frame.f_code: <code object func at 0x764dbfd10370, file "<frozen pyarmor_segv>", line 5>
co_name: func
SEGFAULT ✗
Trial  4: frame.f_code: <code object func at 0x733c5db98370, file "<frozen pyarmor_segv>", line 5>
co_name: func
Computing hash(frame.f_code)...: -1940020248159428493
Success
Normal ✓
Trial  5: frame.f_code: <code object func at 0x7b56701c8370, file "<frozen pyarmor_segv>", line 5>
co_name: func
SEGFAULT ✗
Trial  6: frame.f_code: <code object func at 0x7b871eb74370, file "<frozen pyarmor_segv>", line 5>
co_name: func
Computing hash(frame.f_code)...: -5207489521542538451
Success
Normal ✓
Trial  7: frame.f_code: <code object func at 0x7f8314848370, file "<frozen pyarmor_segv>", line 5>
co_name: func
SEGFAULT ✗
Trial  8: frame.f_code: <code object func at 0x735fb1d6c370, file "<frozen pyarmor_segv>", line 5>
co_name: func
SEGFAULT ✗
Trial  9: frame.f_code: <code object func at 0x74676b578370, file "<frozen pyarmor_segv>", line 5>
co_name: func
SEGFAULT ✗
Trial 10: frame.f_code: <code object func at 0x72a01b358370, file "<frozen pyarmor_segv>", line 5>
co_name: func
SEGFAULT ✗

In IPython scenario (I think it is related to #2293):

  1. IPython exception handler calls get_records() in ultratb.py
  2. Calls stack_data.FrameInfo.stack_data(etb, ...) in ultratb.py
  3. stack_data/core.py calls Source.executing(frame_or_tb)
  4. executing/executing.py creates dict key: key = (frame.f_code, id(code), lasti)
  5. executing_cache.get(key) triggers hash(key)hash(frame.f_code)
  6. PyArmor-corrupted frame.f_code causes SEGFAULT when hashed

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions