andyames-a11y wrote:

Closing out the deeper dive from earlier this session (tracing through our 
actual production code with some added `clang_analyzer_dump` calls), briefly, 
then a new finding that I think is more actionable than that whole thread.

Wrapping up the "why does a pointer get reinterpreted" investigation: traced 
the real (non-repro) crash and confirmed there's no cast anywhere in the 
source. The `void*`-rooted region comes from a custom allocator's `Allocate()`, 
which isn't resolving to a primitive the analyzer specifically recognizes 
(confirmed by comparing region kinds: a trivial allocator that forwards 
directly to ::operator new gets HeapSymRegion and is even correctly 
leak-tracked; ours gets a plain, untracked SymRegion, same as a fully 
opaque/undefined allocation function). So I'd revise my earlier "custom vs std" 
framing — it's more precisely "does the allocation resolve to something the 
analyzer recognizes," not "custom code is inherently disadvantaged." Genuinely 
unsure why our allocator doesn't resolve cleanly; didn't chase that further.

New finding, and I think this one's worth your attention: our repro (the 
Span/Decoder one in the PR's regression test) and your reduced one trigger this 
via different mechanisms. Yours needs an explicit int v = (int)(long)ptr(); — a 
visibly deliberate cast. Ours has no cast anywhere. Added `clang_analyzer_dump` 
calls to see where the pointer-flavored value comes from in ours:

https://godbolt.org/
```
clang_analyzer_dump(inPtr);          // &buffer
clang_analyzer_dump(*inPtr);         // lazyCompoundVal{...,buffer}
scratch[0] = *inPtr;
clang_analyzer_dump(scratch[0]);     // &SymRegion{conj_$1{int *, ...}} [as 32 
bit integer]
```

buffer is uninitialized (`char buffer[1];`, no initializer). Dereferencing it 
doesn't produce a plain unconstrained scalar — it comes back as a 
LazyCompoundVal (normally used for whole-object copies). The pointer-typed 
value only appears once that gets assigned into the differently-typed scratch 
(`int[1]`) — that's where the analyzer conjures the `int *`-typed placeholder, 
with nothing resembling a cast in the source.

So in ours the trigger is just: read an uninitialized array element, assign the 
result into a scalar of a different type. No deliberate reinterpretation needed 
— which might make it a more representative (and more concerning) trigger than 
the explicit cast, since it's the kind of thing that can happen without anyone 
writing anything that looks unusual.

https://github.com/llvm/llvm-project/pull/210200
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to