loader: fix recycled TID overwriting matched calls in dynamic layout#2882
loader: fix recycled TID overwriting matched calls in dynamic layout#2882devs6186 wants to merge 1 commit intomandiant:masterfrom
Conversation
Summary of ChangesHello @devs6186, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a critical bug in the dynamic analysis loader where the recycling of thread IDs by the operating system could lead to data loss and runtime errors. Specifically, it prevents the overwriting of matched calls associated with a recycled TID and ensures that such TIDs are not registered multiple times in the layout. These changes enhance the robustness and accuracy of dynamic analysis results, particularly in environments where TIDs are frequently reused. Additionally, the pull request includes a new test to cover this specific scenario and updates the documentation to provide a clearer overview of capa's output consumption methods. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request addresses a bug where recycled thread IDs in dynamic analysis could lead to overwritten data and subsequent errors. The fix correctly uses setdefault to accumulate calls from all instances of a recycled TID and adds a check to prevent duplicate thread registration. A comprehensive test case has been added to validate the fix. The overall implementation is correct and of high quality. The PR also includes significant code formatting changes, which improve readability. For future PRs, it would be beneficial to separate such large-scale formatting changes from functional bug fixes to make reviewing easier. The documentation updates are also a welcome addition.
When an OS recycles a thread ID (TID) within the same process,
compute_dynamic_layout() was resetting calls_by_thread[t.address] to
an empty list on the second encounter of that ThreadAddress. This
erased the matched calls accumulated from the first thread instance.
Those calls were still present in matched_calls (added by the rule
engine), so the renderer could not locate them in the layout and
raised ValueError("name not found for call").
Fix the overwrite by using setdefault() instead of direct assignment,
and guard the threads_by_process.append() with a membership check so
a recycled TID does not produce a duplicate thread entry in the layout.
A dedicated unit test covering the recycled-TID scenario is added in
tests/test_loader.py.
Fixes mandiant#2619
72dbf97 to
29bb083
Compare
mike-hunhoff
left a comment
There was a problem hiding this comment.
Hi @devs6186, thanks for looking into the data overwriting issue. While this fix prevents matched calls from being lost when a TID is recycled, it doesn't quite address the requirement for unique tracking of distinct thread and process lifecycles. By accumulating all calls under the same ThreadAddress, the layout still fuses separate executions into a single entry, which doesn't solve the core problem described in #2361.
Because we want to avoid merging partial solutions for this component, we'd like to see a more comprehensive fix that addresses the uniqueness and de-duplication problem entirely for both thread and process lifecycles (for example, by incorporating sequence IDs or timestamps into the Address classes).
If you're interested in fully addressing these requirements, we'd love to see those updates in this PR. Alternatively, if you don't have the time or interest to take on the broader scope right now, let us know and we can close this for now.
There was a problem hiding this comment.
This file contains many format-based changes. Please revert these if they are not necessary to pass linting checks.
i appreciate your review, i will look into it moe broadly, i wanna solve a problem completely instead of just submitting a pr, give me days time or two maximum, ill come back with a better solution. Thank you ! |
closes #2619
Root cause
When an OS recycles a thread ID (TID) within the same process,
compute_dynamic_layout()iterated the sameThreadAddresstwice. On the second encounter it executed:The matched calls from the first thread instance were still present in
matched_calls(added by the rule engine), but had been erased from the layout, causing_get_call_name()to raise:Additionally, if both the first and the second instance had matched calls,
t.addresswas appended tothreads_by_process[p.address]twice, creating a duplicate thread entry in the layout.Fix
Two targeted changes in
compute_dynamic_layout()(capa/loader.py):Replace the overwriting assignment with
setdefault()so calls from all instances of a recycled TID accumulate under the same key:Guard the thread-registration block with a membership check so the thread address is only added to
matched_threads/threads_by_processonce, regardless of how many times the TID is recycled:This is the source-level fix requested by the maintainer: the data is now consistent before it reaches the rendering stage, so no exception handling in the renderer is needed.
Test
tests/test_loader.py::test_compute_dynamic_layout_recycled_tidconstructs a minimal mockDynamicFeatureExtractorthat yields the sameThreadAddresstwice (simulating TID recycling), with a matched call only in the first instance. It asserts thatcompute_dynamic_layout()returns a layout containing exactly one thread entry with the matched call intact.Checklist