Bug 100403 - Bogus "function may return address of local variable" warning
Summary: Bogus "function may return address of local variable" warning
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 10.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks: Wreturn-local-addr
  Show dependency treegraph
 
Reported: 2021-05-03 18:52 UTC by lavr
Modified: 2024-03-26 00:07 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description lavr 2021-05-03 18:52:00 UTC
$ gcc --version
gcc (GCC) 10.2.0


$ cat test.c
#include <stdio.h>
#include <string.h>

#define RECLEN  128


struct R {
    int  head;
    int  code;
    char text[1];
};


const char* fun(int n, const struct R* p)
{
    union {
        struct R r;
        char     rec[RECLEN];
    } x;
    const char* err = 0;

    memset(&x, 0, sizeof(x));
    if (p)
        memcpy(&x.r, p, sizeof(*p));
    else
        err = "Invalid argument";

    if (!err) {
        static const char magic[] = "MAGIC";
        const char* msg;
        if (memcmp(x.rec, magic, sizeof(magic)-1) == 0)
            msg = x.rec;
        else if (x.r.text[0])
            msg = x.r.text;
        else
            msg = "Error w/code";
        if (msg)
            printf("%s\n", msg);
        if (x.rec <= msg  &&  msg < x.rec + sizeof(x))
            err = "Error detected";
        else
            err = msg;
    } else
        printf("%s\n", err);
    return err;
}


$ gcc -Wall -O2 -c test.c
test.c: In function ‘fun’:
test.c:45:12: warning: function may return address of local variable [-Wreturn-local-addr]
   45 |     return err;
      |            ^~~
test.c:19:7: note: declared here
   19 |     } x;
      |       ^
test.c:19:7: note: declared here


Noted that does not matter whether "sizeof(x)" or "sizeof(x.rec)" is used at the end of the "if()" statement on line 39.
Comment 1 Andrew Pinski 2021-05-03 19:09:19 UTC
Note, the following condition in the if statement
        if (x.rec <= msg  &&  msg < x.rec + sizeof(x))

Is undefined if msg is not in the range of x.rec[0]...x.rec[RECLEN] .
Comment 2 lavr 2021-05-03 20:14:54 UTC
> undefined if msg is not in the range of x.rec[0]...x.rec[RECLEN]

Indeed for the segmented data address space.  But in most systems it's linear, and the warning is then architecture dependent, and is only expected on those, where the comparison cannot be safely made.  Besides, the warning then should be about that fact, not the return address being of a local variable -- that's misleading, at best.
Comment 3 Martin Sebor 2021-05-03 20:46:22 UTC
The warning behaves as designed.  There's no way for it tell from the IL that the function cannot return the address of x:

  <bb 7> [local count: 375809640]:
  # msg_21 = PHI <"Error w/code"(6), msg_19(10)>
  if (&MEM <char[128]> [(void *)&x + 128B] > msg_21)
    goto <bb 9>; [50.00%]
  else
    goto <bb 8>; [50.00%]

  <bb 8> [local count: 187904820]:

  <bb 9> [local count: 1073741824]:
  # err_3 = PHI <msg_21(8), "Invalid argument"(4), "Error detected"(7), "Error w/code"(6)>
  x ={v} {CLOBBER};
  return err_3;                              <<< -Wreturn-local-addr

  <bb 10> [local count: 375809641]:
  # msg_19 = PHI <&x.r.text(5), &x.rec(3)>   <<< x is on stack
  __builtin_puts (msg_19);
  goto <bb 7>; [100.00%]

}

As Andrew explained, the pointer relational expression is undefined if msg points to the string literal (changing it to equality avoids the warning).  In addition, and arguably more important, GCC's ability to extract useful information from pointer relationships is quite limited so it doesn't "see through" the expression.  The underlying problem (and the limitation) can be reduced to the failure to fold and the false positive for the much simpler test case below.  For a + n to be valid n must be zero or one, and so the function must return null.  If GCC used that to fold the function the warning wouldn't trigger.

With that, I think the report can be resolved as invalid (I'm sure the pointer range optimization has its own bug.)

$ cat z.c && gcc -O2 -S -Wall -fdump-tree-isolate-paths=/dev/stdout z.c
void* f (int n)
{
  char a[1], *p = a + n;
  if (p <= a + 1)
    p = 0;
  return p;
}

;; Function f (f, funcdef_no=0, decl_uid=1943, cgraph_uid=1, symbol_order=0)

z.c: In function ‘f’:
z.c:6:10: warning: function may return address of local variable [-Wreturn-local-addr]
    6 |   return p;
      |          ^
z.c:3:8: note: declared here
    3 |   char a[1], *p = a + n;
      |        ^

SSA replacement table
N_i -> { O_1 ... O_j } means that N_i replaces O_1, ..., O_j

p_7 -> { p_2 }
.MEM_8 -> { .MEM_6 }
Incremental SSA update started at block: 2
Number of blocks in CFG: 6
Number of blocks to update: 2 ( 33%)


Removing basic block 3
void * f (int n)
{
  char * p;
  char a[1];
  sizetype _1;

  <bb 2> [local count: 1073741824]:
  _1 = (sizetype) n_3(D);
  p_4 = &a + _1;
  if (&MEM <char[1]> [(void *)&a + 1B] >= p_4)
    goto <bb 3>; [100.00%]
  else
    goto <bb 4>; [0.00%]

  <bb 3> [local count: 311385128]:
  # p_2 = PHI <0B(2)>
  a ={v} {CLOBBER};
  return p_2;

  <bb 4> [count: 0]:
  # p_7 = PHI <p_4(2)>
  a ={v} {CLOBBER};
  return 0B;

}
Comment 4 Martin Sebor 2021-05-03 20:49:45 UTC
The pointer limitation is discussed in more detail in pr91227.