[PATCH 1/2] add -Wuse-after-free

Martin Sebor msebor@gmail.com
Tue Nov 2 17:09:49 GMT 2021

On 11/1/21 11:32 PM, Eric Gallager wrote:
> On Mon, Nov 1, 2021 at 6:18 PM Martin Sebor via Gcc-patches
> <gcc-patches@gcc.gnu.org> wrote:
>> Patch 1 in the series detects a small subset of uses of pointers
>> made indeterminate by calls to deallocation functions like free
>> or C++ operator delete.  To control the conditions the warnings
>> are issued under the new -Wuse-after-free= option provides three
>> levels.  At the lowest level the warning triggers only for
>> unconditional uses of freed pointers and doesn't warn for uses
>> in equality expressions.  Level 2 warns also for come conditional
>> uses, and level 3 also for uses in equality expressions.
>> I debated whether to make level 2 or 3 the default included in
>> -Wall.  I decided on 3 for two reasons: 1) to raise awareness
>> of both the problem and GCC's new ability to detect it: using
>> a pointer after it's been freed, even only in principle, by
>> a successful call to realloc, is undefined, and 2) because
>> it's trivial to lower the level either globally, or locally
>> by suppressing the warning around such misuses.
>> I've tested the patch on x86_64-linux and by building Glibc
>> and Binutils/GDB.  It triggers a number of times in each, all
>> due to comparing invalidated pointers for equality (i.e., level
>> 3).  I have suppressed these in GCC (libiberty) by a #pragma,
>> and will see how the Glibc folks want to deal with theirs (I
>> track them in BZ #28521).
>> The tests contain a number of xfails due to limitations I'm
>> aware of.  I marked them pr?????? until the patch is approved.
>> I will open bugs for them before committing if I don't resolve
>> them in a followup.
>> Martin
> Hi, I'm just wondering how this fares compared to the static
> analyzer's -Wanalyzer-use-after-free; could you compare and contrast
> them for us?

Good question.

The analyzer does a far more exhaustive, interprocedural
analysis of (most) paths through a program, symbolically
evaluating the conditions under which statements are
evaluated to determine reachability.

This initial implementation of -Wuse-after-free does only
a superficial analysis of a few nearby statements in a single
function (plus those inlined into it), those with direct
dependencies of uses on the deallocation statements.  It
doesn't do any evaluation of conditions which limits how
far it can go in its checking.  If it sees a pointer used
after a free call as in

   free (p);
   return *p;   // used after free (level 1)

it triggers.  If it sees a conditional use as in

   free (p);
   if (cond)
     return *p;   // may be used after free (level 2)

it triggers at level 2, but only if the free is unconditional
and flows directly into the condition guarding the use.  If
the free is guarded by another condition it doesn't trigger:

   if (c_1)
     free (p);

   if (c_2)
     return *p;

This last case is the consequence of not doing any condition
evaluation (c_1 could be mutually exclusive with c_2).  Adding
support for it is a future enhancement, something I'm out of
time for in this stage 1 but I'd like to tackle for GCC 13.
Both GCC's and Clang's analyzers detect all three cases.

Unlike GCC's analyzer (but like Clang's), this new warning
flags all pointer uses, not just their derefernces (operands
of equality expressions only at level 3; Clang doesn't seem
to diagnose uses in equality tests at all).  So unlike
GCC's analyzer, it will trigger in the first two example
above even if it's the pointer itself being returned and not
what it pointed to before it was freed .  This is because
a freed pointer is invalid the same way an uninitialized
variable is invalid, and using it in any way is strictly
undefined  (returning it from a function or passing as
an argument to another can lead to memory corruption or
unintentional information disclosure).


PS The distinction between levels 1 (unconditional uses)
and 2 (conditional) is more like that between the levels
of -Warray-bounds -- level 2 catching slightly more cases
at the expense of potentially some, albeit rare, false
positives.  It's not like between -Wuninitialized and
-Wmaybe-uninitialized where the uninitialized checker
employs limited symbolic predicate analysis and the need
for separate options came out of the desire to be able to
control separately the false positives due to
the imperfections inherent in this strategy (or incidental
to its implementation in GCC: the limited ability to
determine that two conditions are mutually exclusive.

More information about the Gcc-patches mailing list