[clang] [analyzer] Avoid use of `CallEvent`s with obsolete state (PR #160707)
DonĂ¡t Nagy via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 29 04:30:16 PDT 2025
NagyDonat wrote:
> I agree that CallEvents have this trap. I've seen it. I don't think it's too widespread. I think we usually catch the use of stale states of CallEvents during review.
Noted. It is true that good enough code review can prevent any bugs, but I still think that we should prefer solutions that automatically prevent the errors.
> It also helps that the State is part of our vocabulary and its passed liberally as a parameter, which sets a good example.
If the `State` is passed around liberally (to ensure that we always inject the most recent one), then why do we have a second `State` within the `CallEvent` as well? I would prefer an approach where `CallEvent` doesn't own a state (and perhaps there is a `CallEventWithState` type if there are use cases where that is needed).
> This is a critical piece of code. Many of the changed lines look aesthetic, aka. refactor.
I had to change lots of lines because I needed to switch between use of `const CallEvent &` (a reference type, the members can be accessed by dot) and `CallEventRef<>` (a pointer type, members can be accessed by arrow).
> You mention some bug if I recall my sloppy reading, but I could not spot behavioral changes or at least it didn't stand out to me.
There are three bugs that are fixed by this commit:
(1) Tthe `eval::Call` checkers were called with a `CallEvent` object whose associated state was obsolete: it definitely didn't include the state changes from the PreCall checkers and (according to the comment at the beginning of `ExprEngine::EvalCall` might have been even more obsolete).
(2) The use of `Call.getArgSVal(Arg)` within the "run PointerEscape for the newly conjured symbols" part ([at the end of the `ExprEngine::evalCall`](https://github.com/llvm/llvm-project/blob/9552e899e494e619093e8685173a4af0ba73e049/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp#L725)) relies on the state within the `CallEvent` (which may have been obsolete even at the beginning of this method call) instead of the variable `State` (which is up-to date and corresponds to one of the potentially multiple nodes resulting from the pre/eval/post steps).
(3) A bit later (still in the "run PointerEscape for the newly conjured symbols" step) the `CallEvent` with the ancient state is also passed to `processPointerEscapedOnBind` which directly forwards it to checkers (without updating the state).
> If this is NFC, let's adjust the PR title. If has behavioral change, let's have a test exposing that and split off the refactor parts.
I will create a test with a new debug checker which modifies the state in its `PreCall` callback and validates that the modification is visible through the `CallEvent` objects received by the `EvalCall` and `PointerEscape` callbacks. Is this sufficient, or would you prefer a different kind of test?
I don't think that it is possible to meaningfully simplify this commit by splitting off "refactor parts". There are only a few non-essential changes (comment changes, renamed variables) and without them the "core" change would be more difficult to understand.
https://github.com/llvm/llvm-project/pull/160707
More information about the cfe-commits
mailing list