Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[3.13] gh-128679: Fix tracemalloc.stop() race conditions#128897

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
vstinner merged 6 commits intopython:3.13fromvstinner:tracemalloc_stop13
Jan 18, 2025

Conversation

@vstinner
Copy link
Member

@vstinnervstinner commentedJan 15, 2025
edited by bedevere-appbot
Loading

tracemalloc_alloc(), tracemalloc_realloc(), PyTraceMalloc_Track(), PyTraceMalloc_Untrack() and _PyTraceMalloc_TraceRef() now check tracemalloc_config.tracing after calling TABLES_LOCK().

_PyTraceMalloc_Stop() now protects more code with TABLES_LOCK(), especially setting tracemalloc_config.tracing to 1.

Add a test using PyTraceMalloc_Track() to test tracemalloc.stop() race condition.

tracemalloc_alloc(), tracemalloc_realloc(), PyTraceMalloc_Track(),PyTraceMalloc_Untrack() and _PyTraceMalloc_TraceRef() now checktracemalloc_config.tracing after calling TABLES_LOCK()._PyTraceMalloc_Stop() now protects more code with TABLES_LOCK(),especially setting tracemalloc_config.tracing to 1.Add a test using PyTraceMalloc_Track() to test tracemalloc.stop()race condition.
@vstinnervstinner marked this pull request as ready for reviewJanuary 16, 2025 12:53
@vstinner
Copy link
MemberAuthor

@ZeroIntensity: Here is a simplified change for the 3.12 and 3.13 branch. Would you mind to review it?

Copy link
Member

@ZeroIntensityZeroIntensity left a comment

Choose a reason for hiding this comment

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

There's a couple other (unmodified) functions where I'm seeing lockless reads:

  • tracemalloc_get_traceback
  • _PyMem_DumpTraceback
  • _PyTraceMalloc_GetTraces
  • _PyTraceMalloc_GetTracedMemory
  • _PyTraceMalloc_ResetPeak


intres=-1;

TABLES_LOCK();

Choose a reason for hiding this comment

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

We also readtracing without a lock before this.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Checkingtracing without the lock is a workaround for the Python finalization. TABLES_LOCK() can no longer be called after _PyTraceMalloc_Fini() (the lock is destroyed), whereas _PyTraceMalloc_TraceRef() is still called after _PyTraceMalloc_Fini().

tracing is checked again once we acquired the lock.

The issue was fixed properly in the main branch by calling(void)PyRefTracer_SetTracer(NULL, NULL); in_PyTraceMalloc_Stop(). Since this change is designed to be backported to Python 3.12, I didn't include this fix.

Once this change is merged in 3.13 and 3.12, we can remove the unprotectedtracing read in 3.13 in_PyTraceMalloc_TraceRef() by adding(void)PyRefTracer_SetTracer(NULL, NULL);.

ZeroIntensity reacted with thumbs up emoji
@vstinner
Copy link
MemberAuthor

Ok, I completed my PR to cover most (almost all?) functions, instead of focusing on alloc/realloc/free and Track/Untrack functions.

@ZeroIntensity: Please review the updated PR.

@vstinner
Copy link
MemberAuthor

vstinner commentedJan 16, 2025
edited
Loading

_PyTraceMalloc_GetTraces

Fixing properly_PyTraceMalloc_GetTraces() requires to redesigntracemalloc! For example, currently, thePyMem_Malloc() hook usesTABLES_LOCK() on a reentrant call. In the main branch, the whole _PyTraceMalloc_GetTraces() function is run withTABLES_LOCK(). If we do the same in 3.13, thePyMem_Malloc() hook must be modified to no longer useTABLES_LOCK() on a reentrant call. It would change the behavior (for correctness!), I'm not sure that it's a good thing in a stable branch.

In the main branch, I redesigned the code to be able to hold the lock for all tables operations. For that, I had to modify the behavior:

tracemalloc_realloc() and tracemalloc_free() no longer remove the trace on reentrant call.

I'm open to consider fixing_PyTraceMalloc_GetTraces() by redesigningtracemalloc in 3.13+3.12, but I'm not sure if it's worth it. Callingget_traces() is a deliberate action from the user, it's different from thetracemalloc hooks which called implicitly by running regular Python code.

ZeroIntensity reacted with thumbs up emoji

@ZeroIntensity
Copy link
Member

It looks like tests somehow were broken.

So TABLES_LOCK() can be called in _PyMem_DumpTraceback() even iftracemalloc is not tracing.
@vstinner
Copy link
MemberAuthor

It looks like tests somehow were broken.

Fixed.

Well... Fixing more functions require to backport more changes. I backported the_PyTraceMalloc_Init() change to fix test_capi and test_gc.

Copy link
Member

@ZeroIntensityZeroIntensity left a comment

Choose a reason for hiding this comment

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

LGTM. With the exception of a few special cases that you've mentioned, everything is properly locked! Due to the changes to the lifecycle and the size of the change, I think we should run buildbots before merging.

vstinner reacted with thumbs up emoji
@ZeroIntensityZeroIntensity added the 🔨 test-with-buildbotsTest PR w/ buildbots; report in status section labelJan 16, 2025
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by@ZeroIntensity for commit54c8f9e 🤖

If you want to schedule another build, you need to add the🔨 test-with-buildbots label again.

@bedevere-botbedevere-bot removed the 🔨 test-with-buildbotsTest PR w/ buildbots; report in status section labelJan 16, 2025
@vstinner
Copy link
MemberAuthor

buildbot/AMD64 FreeBSD Refleaks PR failed

test_tracemalloc_track_race (test.test_tracemalloc.TestCAPI.test_tracemalloc_track_race) ...Debug memory block at address p=0x82647c840: API 'r'    16 bytes originally requested    The 7 pad bytes at p-7 are FORBIDDENBYTE, as expected.    The 8 pad bytes at tail=0x82647c850 are FORBIDDENBYTE, as expected.    Data at p: 10 e3 a6 42 08 00 00 00 60 c9 47 26 08 00 00 00Enable tracemalloc to get the memory block allocation tracebackFatal Python error: _PyMem_DebugRawFree: bad ID: Allocated using API 'r', verified using API 'P'Python runtime state: initializedThread 0x0000000831196500 (most recent call first):  <no Python frame>(...)

@vstinnervstinner marked this pull request as draftJanuary 16, 2025 17:00
@vstinner
Copy link
MemberAuthor

I mark this PR as a draft because of the FreeBSD crash.

Oh, I can also reproduce thetest_tracemalloc_track_race() crash on the main branch on FreeBSD.

ZeroIntensity reacted with thumbs up emoji

@vstinner
Copy link
MemberAuthor

So far, I cannot reproduce the crash on Linux.

@vstinner
Copy link
MemberAuthor

I can trigger a crash if I insert a delay inPyMem_SetAllocator():

diff --git a/Objects/obmalloc.c b/Objects/obmalloc.cindex b103deb01ca..c20eb47e8a8 100644--- a/Objects/obmalloc.c+++ b/Objects/obmalloc.c@@ -859,7 +859,14 @@ set_allocator_unlocked(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator) {     switch(domain)     {-    case PYMEM_DOMAIN_RAW: _PyMem_Raw = *allocator; break;+    case PYMEM_DOMAIN_RAW:+        _PyMem_Raw.ctx = allocator->ctx;+        (void)sched_yield();+        _PyMem_Raw.malloc = allocator->malloc;+        _PyMem_Raw.calloc = allocator->calloc;+        _PyMem_Raw.realloc = allocator->realloc;+        _PyMem_Raw.free = allocator->free;+        break;     case PYMEM_DOMAIN_MEM: _PyMem = *allocator; break;     case PYMEM_DOMAIN_OBJ: _PyObject = *allocator; break;     /* ignore unknown domain */

The write is protected by a lock (write), butPyMem_RawMalloc() andPyMem_RawFree() don't use the lock (read).

@vstinner
Copy link
MemberAuthor

vstinner commentedJan 18, 2025
edited
Loading

Only debug build are affected, not release build. I modified the test in the main branch to skip it on debug build:#128988

ZeroIntensity reacted with thumbs up emoji

…ython#128988)There is a race condition between PyMem_SetAllocator() andPyMem_RawMalloc()/PyMem_RawFree(). While PyMem_SetAllocator() writeis protected by a lock, PyMem_RawMalloc()/PyMem_RawFree() reads arenot protected by a lock. PyMem_RawMalloc()/PyMem_RawFree() can becalled with an old context and the new function pointer.On a release build, it's not an issue since the context is not used.On a debug build, the debug hooks use the context and so can crash.(cherry picked from commit9bc1964)
@vstinnervstinner marked this pull request as ready for reviewJanuary 18, 2025 23:19
@vstinnervstinnerenabled auto-merge (squash)January 18, 2025 23:20
@vstinnervstinner merged commit6b47499 intopython:3.13Jan 18, 2025
39 checks passed
@vstinnervstinner deleted the tracemalloc_stop13 branchJanuary 18, 2025 23:39
@miss-islington-app
Copy link

Thanks@vstinner for the PR 🌮🎉.. I'm working now to backport this PR to: 3.12.
🐍🍒⛏🤖

@miss-islington-app
Copy link

Sorry,@vstinner, I could not cleanly backport this to3.12 due to a conflict.
Please backport usingcherry_picker on command line.

cherry_picker 6b47499510e47c0401d1f6cca2660fc12c496e39 3.12

@bedevere-app
Copy link

GH-129022 is a backport of this pull request to the3.12 branch.

@bedevere-appbedevere-appbot removed the needs backport to 3.12only security fixes labelJan 19, 2025
vstinner added a commit that referenced this pull requestJan 19, 2025
…129022)[3.13]gh-128679: Fix tracemalloc.stop() race conditions (#128897)tracemalloc_alloc(), tracemalloc_realloc(), PyTraceMalloc_Track(),PyTraceMalloc_Untrack() and _PyTraceMalloc_TraceRef() now checktracemalloc_config.tracing after calling TABLES_LOCK()._PyTraceMalloc_Stop() now protects more code with TABLES_LOCK(),especially setting tracemalloc_config.tracing to 1.Add a test using PyTraceMalloc_Track() to test tracemalloc.stop()race condition.Call _PyTraceMalloc_Init() at Python startup.(cherry picked from commit6b47499)
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@ZeroIntensityZeroIntensityZeroIntensity approved these changes

@ericsnowcurrentlyericsnowcurrentlyAwaiting requested review from ericsnowcurrentlyericsnowcurrently is a code owner

Assignees

@vstinnervstinner

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

3 participants

@vstinner@ZeroIntensity@bedevere-bot

[8]ページ先頭

©2009-2025 Movatter.jp