Hi David, I've been playing around with sm-malloc on C++ samples. I've noticed double delete don't go under -Wanalyzer-double-free but within -Wanalyzer-use-after-free scope.
With the reproducer ... > struct A {}; > > int main() { > A* a = new A(); > delete a; > delete a; > return 0; > } ... the analyzer diagnoses: build/gcc/xg++ -Bbuild/gcc -S -fanalyzer -Wanalyzer-mismatching-deallocation -Wanalyzer-use-after-free -Wanalyzer-double-free -Wanalyzer-malloc-leak double_delete_test.cpp double_delete_test.cpp: In function ‘int main()’: double_delete_test.cpp:7:1: warning: use after ‘delete’ of ‘a’ [CWE-416] [-Wanalyzer-use-after-free] 7 | } | ^ ‘int main()’: events 1-8 | | 3 | A* a = new A(); | | ^ | | | | | (1) allocated here | | (2) assuming ‘operator new(8)’ is non-NULL | 4 | delete a; | | ~~~~~~~~ | | | | | | | (4) ...to here | | | (5) deleted here | | (3) following ‘true’ branch... | 5 | delete a; | | ~~~~~~~~ | | | | | | | (7) ...to here | | (6) following ‘true’ branch... | 6 | return 0; | 7 | } | | ~ | | | | | (8) use after ‘delete’ of ‘a’; deleted at (5) | double_delete_test.cpp:3:18: warning: dereference of possibly-NULL ‘operator new(8)’ [CWE-690] [-Wanalyzer-possible-null-dereference] 3 | A* a = new A(); | ^ ‘int main()’: events 1-2 | | 3 | A* a = new A(); | | ^ | | | | | (1) this call could return NULL | | (2) ‘operator new(8)’ could be NULL: unchecked value from (1) | Debugging read out two things: - During second call of 'on_deallocator_call' on delete, the state would be 'stop' instead of the expected 'freed' - The call to set_next_state transitions malloc instead of delete from 'nonnull' to 'freed'. I'm still trying to come up with why these two behaviors happens. By the way, in the first call to 'on_deallocator_call' on delete, the state is set to 'nonnull', which conforms to C++ behavior for new. However, a -Wanalyzer-possible-null-dereference is emitted at the expression 'new'. I haven't yet figured out why, but I'm looking into it. Another observation was in the distinction between delete and free in the case of mixing them. With 'a' being allocated with malloc: > A* a = (A*) malloc(sizeof(A)); > free(a); > delete a; // -Wanalyzer-use-after-free, OK, might expect warning for double free though ? but with allocation through new and inversion of free/delete > A* a = new A(); > delete a; > free(a); // No diagnostics at all from the analyzer, got -Wmismatched-new-delete from the front-end though. I believe this might come from a similar issue as above, but I still have to investigate on that front. I just noticed another unexpected behavior. Let's consider > struct A {int x; int y;}; > void* foo() { A* a = (A*) __builtin_malloc(sizeof(A)); return a; } > int main() { > A* a2 = (A*) __builtin_malloc(sizeof(A)); > foo(); > return 0; > } Then a -Wanalyzer-malloc-leak is correctly ensued for allocation in foo(), but none is emitted for the leak in main(), although I checked the exploded graph it is correctly marked as unchecked(free). Should I file those on bugzilla ? Also I have taken interest in PR106388 - Support for use-after-move in -fanalyzer -. The prerequisite PR106386 - Reuse libstdc++ assertions - would also be of great help in extending the support of -Wanalyzer-out-of-bounds, as we could detect out-of-bounds on vectors through __glibcxx_requires_subscript used in the definition of operator[]. I already thought about a few ideas to implement that, but I'll develop them more and try to come up with some proof of concept before sending them to you. Hopefully by tomorrow I'll update on this, I'll preferably hop to bed now haha. Do you think this could make a suitable GSoC subject ? Best, Benjamin.