fix: Put a sentinel in the native thread local error slot - #312
Draft
tmathern wants to merge 8 commits into
Draft
Conversation
tmathern
marked this pull request as draft
August 25, 2026 06:03
tmathern
force-pushed
the
mathern/error-slot-sentinel
branch
from
August 25, 2026 22:02
81fed3d to
ac6a4da
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes in this pull request
After a consuming native call fails, the Python side decides whether the handle was taken by reading the last native error and matching it against the pre-consume tags (
UntrackedPointer:,WrongPointerType:). That slot is thread-local (cimpl_error.rs,LAST_ERROR) and sticky: nothing clears it between calls. A pooled worker thread therefore carries whatever error it last set, across unrelated tasks. A later failure that does not set its own error reads a tag from a task that finished long ago. THis leads to wrong decisions regarding what to release actually when.This is especially problematic if an address was reused (which can happen under heavier load): the registry rejects an
address it no longer tracks, unless another thread has allocated a fresh tracked object at that exact address, in which case the stale free finds a live entry and destroys a different thread's object.
Repeated allocation and release of similarly sized objects is precisely the pattern that makes an allocator hand back an address it just freed. This leads either to bad references, or allocator-detected corruptions.
Put a sentinel in the error slot so the failing path can be identified, and to make sure no stale error is visible (which could lead to the wrong memory release decisions).
As a sidenote: On any consume failure where the verdict is not certain, this takes the consumed branch, meaning leaks could appear. Also, note this will disappear with v0.91.0 of c2pa-rs, which clarifies the consumption status of pointers (always consumed by native calls), make sure the leak situation can't happen anymore. It is still worthwhile to consider this fix, to ensure no stale errors are read by churning threads.
Checklist
TO DOitems (or similar) have been entered as GitHub issues and the link to that issue has been included in a comment.