public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c/106119] New: Bogus use-after-free warning triggered by optimizer
@ 2022-06-28 14:28 tom.cosgrove at arm dot com
  2022-06-28 15:37 ` [Bug c/106119] " tom.cosgrove at arm dot com
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: tom.cosgrove at arm dot com @ 2022-06-28 14:28 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106119

            Bug ID: 106119
           Summary: Bogus use-after-free warning triggered by optimizer
           Product: gcc
           Version: 12.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: tom.cosgrove at arm dot com
  Target Milestone: ---

There are a number of bug reports related to -Wuse-after-free - it's not clear
to me if our particular issue has already been captured, so please do close as
duplicate if it has (it seems that the optimiser is moving code around, so
could potentially be a duplicate of
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104215).

Assuming not a duplicate, this should probably be linked to
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104075.


Our self-test code checks to see if two consecutive calloc() calls return the
same value, by storing the pointer value in a uintptr_t integer and comparing
against this value later. When we compile with optimisation (we don't see the
problem with -O0) and with an if-statement later in the code, we get a spurious
use-after-free warning.

We don't get this warning at -O0, or if we remove the if-statement.

We have also confirmed that if we reorder the assignment to the uintptr_t and
the call to free() we consistently DO get the use-after-free warning (as
expected) regardless of optimisation level.


gcc-12 $ uname -a
Linux e120653 5.17.15-1-MANJARO #1 SMP PREEMPT Wed Jun 15 07:09:31 UTC 2022
x86_64 GNU/Linux

gcc-12 $ gcc --version
gcc (GCC) 12.1.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

gcc-12 $ cat foo.c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

void calloc_self_test( __attribute__ ((unused)) int verbose )
{
    void *buffer1 = calloc( 1, 1 );
    uintptr_t old_buffer1 = (uintptr_t) buffer1;
    free( buffer1 );
    buffer1 = calloc( 1, 1 );
    int same = ( old_buffer1 == (uintptr_t) buffer1 );
    if( verbose )
        printf( "  CALLOC(1 again): passed (%s address)\n",
                same ? "same" : "different" );
    free( buffer1 );
}

No warning at -O0:

gcc-12 $ gcc -O0 -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -o foo -c
foo.c
gcc-12 $

But unexpected warning at -O2:

gcc-12 $ gcc -O2 -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -o foo -c
foo.c
foo.c: In function ‘calloc_self_test’:
foo.c:8:15: warning: pointer ‘buffer1’ may be used after ‘free’
[-Wuse-after-free]
    8 |     uintptr_t old_buffer1 = (uintptr_t) buffer1;
      |               ^~~~~~~~~~~
foo.c:9:5: note: call to ‘free’ here
    9 |     free( buffer1 );
      |     ^~~~~~~~~~~~~~~

Removing the if-statement quashes the warning:

gcc-12 $ perl -ni -e 'print unless /if.*verbose/' foo.c
gcc-12 $ cat foo.c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

void calloc_self_test( __attribute__ ((unused)) int verbose )
{
    void *buffer1 = calloc( 1, 1 );
    uintptr_t old_buffer1 = (uintptr_t) buffer1;
    free( buffer1 );
    buffer1 = calloc( 1, 1 );
    int same = ( old_buffer1 == (uintptr_t) buffer1 );
        printf( "  CALLOC(1 again): passed (%s address)\n",
                same ? "same" : "different" );
    free( buffer1 );
}

gcc-12 $ gcc -O0 -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -o foo -c
foo.c
gcc-12 $ gcc -O2 -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -o foo -c
foo.c
gcc-12 $


Checking that we always get the error when we swap the assignment and free()
statements:

gcc-12 $ cat bar.c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

void calloc_self_test( __attribute__ ((unused)) int verbose )
{
    void *buffer1 = calloc( 1, 1 );
    free( buffer1 );
    uintptr_t old_buffer1 = (uintptr_t) buffer1;
    buffer1 = calloc( 1, 1 );
    int same = ( old_buffer1 == (uintptr_t) buffer1 );
    if( verbose )
        printf( "  CALLOC(1 again): passed (%s address)\n",
                same ? "same" : "different" );
    free( buffer1 );
}

gcc-12 $ gcc -O0 -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -o bar -c
bar.c
bar.c: In function ‘calloc_self_test’:
bar.c:9:15: warning: pointer ‘buffer1’ used after ‘free’ [-Wuse-after-free]
    9 |     uintptr_t old_buffer1 = (uintptr_t) buffer1;
      |               ^~~~~~~~~~~
bar.c:8:5: note: call to ‘free’ here
    8 |     free( buffer1 );
      |     ^~~~~~~~~~~~~~~

gcc-12 $ gcc -O2 -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -o bar -c
bar.c
bar.c: In function ‘calloc_self_test’:
bar.c:9:15: warning: pointer ‘buffer1’ used after ‘free’ [-Wuse-after-free]
    9 |     uintptr_t old_buffer1 = (uintptr_t) buffer1;
      |               ^~~~~~~~~~~
bar.c:8:5: note: call to ‘free’ here
    8 |     free( buffer1 );

and we still get the warning (as we would hope) without the if-statement:

gcc-12 $ perl -ni -e 'print unless /if.*verbose/' bar.c
gcc-12 $ cat bar.c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

void calloc_self_test( __attribute__ ((unused)) int verbose )
{
    void *buffer1 = calloc( 1, 1 );
    free( buffer1 );
    uintptr_t old_buffer1 = (uintptr_t) buffer1;
    buffer1 = calloc( 1, 1 );
    int same = ( old_buffer1 == (uintptr_t) buffer1 );
        printf( "  CALLOC(1 again): passed (%s address)\n",
                same ? "same" : "different" );
    free( buffer1 );
}

gcc-12 $ gcc -O0 -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -o bar -c
bar.c
bar.c: In function ‘calloc_self_test’:
bar.c:9:15: warning: pointer ‘buffer1’ used after ‘free’ [-Wuse-after-free]
    9 |     uintptr_t old_buffer1 = (uintptr_t) buffer1;
      |               ^~~~~~~~~~~
bar.c:8:5: note: call to ‘free’ here
    8 |     free( buffer1 );
      |     ^~~~~~~~~~~~~~~
gcc-12 $ gcc -O2 -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -o bar -c
bar.c
bar.c: In function ‘calloc_self_test’:
bar.c:9:15: warning: pointer ‘buffer1’ used after ‘free’ [-Wuse-after-free]
    9 |     uintptr_t old_buffer1 = (uintptr_t) buffer1;
      |               ^~~~~~~~~~~
bar.c:8:5: note: call to ‘free’ here
    8 |     free( buffer1 );
      |     ^~~~~~~~~~~~~~~

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2024-03-15  1:23 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-28 14:28 [Bug c/106119] New: Bogus use-after-free warning triggered by optimizer tom.cosgrove at arm dot com
2022-06-28 15:37 ` [Bug c/106119] " tom.cosgrove at arm dot com
2022-06-29 13:53 ` tom.cosgrove at arm dot com
2022-07-11 22:07 ` [Bug tree-optimization/106119] [12/13 Regression] " pinskia at gcc dot gnu.org
2022-07-25 15:50 ` rguenth at gcc dot gnu.org
2022-10-17 12:31 ` rguenth at gcc dot gnu.org
2022-12-15  0:37 ` pinskia at gcc dot gnu.org
2023-05-08 12:24 ` [Bug tree-optimization/106119] [12/13/14 " rguenth at gcc dot gnu.org
2024-03-15  1:23 ` [Bug tree-optimization/106119] [12 " law at gcc dot gnu.org

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).