Skip to content

Code object de-instrumentation can dereference missing monitoring data #155295

Description

@taegyunkim

Bug description

Origin and AI assistance

This failure was initially identified while reviewing production crash logs available to Datadog. The investigation, reproducer, proposed patch, tests, and this issue write-up were produced primarily with GPT-5.6 Sol (high), with human direction, review, and local validation.

A code object containing INSTRUMENTED_LINE or INSTRUMENTED_INSTRUCTION without the corresponding monitoring data can cause a null-pointer dereference when CPython tries to de-instrument its bytecode.

CodeType.replace() is a low-level API and permits malformed bytecode, but other malformed opcodes fail with SystemError when used. Runtime-only instrumented opcodes should likewise fail safely rather than terminate the process.

On current main, this Python-only reproducer crashes when reading code.co_code:

import dis


def func():
    pass


bytecode = bytearray(func.__code__.co_code)
bytecode[0] = dis._all_opmap["INSTRUMENTED_LINE"]
code = func.__code__.replace(co_code=bytes(bytecode))
code.co_code

The same applies to INSTRUMENTED_INSTRUCTION.

The native path on a debug build from main at c3aefdb9eff0734058376b96fc86d89b1a345d75 is:

_Py_GetBaseCodeUnit
  -> deopt_code
    -> _PyCode_GetCode
      -> code.co_code getter

_Py_GetBaseCodeUnit() assumes that code->_co_monitoring and the opcode-specific monitoring array are non-null:

if (opcode == INSTRUMENTED_LINE) {
    opcode = _PyCode_GetOriginalOpcode(code->_co_monitoring->lines, i);
}
if (opcode == INSTRUMENTED_INSTRUCTION) {
    opcode = code->_co_monitoring->per_instruction_opcodes[i];
}

A newly constructed code object has _co_monitoring == NULL, so materializing its public bytecode dereferences a null pointer.

I would expect code.co_code to raise SystemError for this inconsistent internal state. I have a patch that validates the required monitoring data in deopt_code() before calling _Py_GetBaseCodeUnit().

Observed results:

  • CPython 3.12.13: SIGSEGV
  • CPython 3.13.14: SIGSEGV
  • CPython 3.14.6: SIGSEGV
  • CPython 3.16.0a0 from main at c3aefdb9ef: SIGSEGV

CPython versions tested on

3.12, 3.13, 3.14, 3.16, CPython main branch

Operating systems tested on

Linux

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions