Skip to content

Conversation

@OutisNemosseus
Copy link

@OutisNemosseus OutisNemosseus commented Jan 8, 2026

Fix use-after-free in _lsprof module when external timer's __index__ method clears the profiler during callback.

Problem

prof._pystart_callback installs a new ProfilerContext and calls the external timer. If the timer's __index__ clears the profiler, it frees currentProfilerContext while initContext or Stop still writes to it, causing heap-use-after-free.

Solution

Added inCallback flag to ProfilerObject to prevent clearEntries from freeing memory while timer callback is executing.

Changes

  • Added int inCallback field to ProfilerObject struct
  • Set flag before/after call_timer() in initContext and Stop
  • Check flag at start of clearEntries to prevent freeing during callback
  • Added test case test_lsprof_uaf.py

Closes #143545

@bedevere-app
Copy link

bedevere-app bot commented Jan 8, 2026

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@python-cla-bot
Copy link

python-cla-bot bot commented Jan 8, 2026

The following commit authors need to sign the Contributor License Agreement:

CLA not signed

ProfilerEntry *ctxEntry;
} ProfilerContext;


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid changing the formatting of code that you do not touch, as this makes review and backporting more difficult.

Comment on lines +329 to +331
pObj->inCallback = 1;
self->t0 = call_timer(pObj);
pObj->inCallback = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation is inaccurate.

@@ -0,0 +1 @@
Fix use-after-free in the ``_lsprof`` module when external timer's ``__index__`` method clears the profiler during callback.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Fix use-after-free in the ``_lsprof`` module when external timer's ``__index__`` method clears the profiler during callback.
Fix use-after-free in the :mod:`_lsprof` module when external timer's ``__index__`` method clears the profiler during callback.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether we should mention _lsprof here, because it's a private module that users should not use directly. Users interact with it via profile and cProfile, so I think we should describe the news entry in terms of those two modules instead.

@@ -0,0 +1,37 @@
"""Test for gh-143545: UAF in lsprof via re-entrant external timer."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although this happens in the _lsprof module, the user interface and the test code are related to cProfile (and profile), so I think we should move the test case to the test files for those modules.

double externalTimerUnit;
int tool_id;
PyObject* missing;
int inCallback;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are using C11, we can use bool in <stdbool.h> for boolean values.

try:
prof._pystart_callback(victim.__code__, 0)
except (RuntimeError, ValueError):
pass # Expected if we block the operation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is expected - maybe we should use with self.assertRaisesRegex?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use-after-free in lsprof initContext via re-entrant external timer __index__

3 participants