Bug 51744 - Erroneous warning: memset used with constant zero length parameter
Summary: Erroneous warning: memset used with constant zero length parameter
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: lto (show other bugs)
Version: 4.6.2
: P3 major
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic, lto
Depends on:
Blocks:
 
Reported: 2012-01-03 20:04 UTC by Richard Newton
Modified: 2014-08-07 01:05 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 4.7.3, 4.8.3, 4.9.0
Last reconfirmed: 2012-01-04 00:00:00


Attachments
concatated ma2.i and ma2_test.i from -save-temps ma2.c and ma2_test.c (7.15 KB, application/octet-stream)
2012-01-03 20:04 UTC, Richard Newton
Details
MUCH simpler test case (2.97 KB, text/plain)
2012-10-23 16:42 UTC, Mark Pizzolato
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Richard Newton 2012-01-03 20:04:50 UTC
Created attachment 26233 [details]
concatated ma2.i and ma2_test.i from -save-temps ma2.c and ma2_test.c

Attached code gives erroneous warning when compiled with -
gcc -O3 -flto -Wall -Wextra -D_FORTIFY_SOURCE=2 -c ma2.c
gcc -O3 -flto -Wall -Wextra -D_FORTIFY_SOURCE=2 -c ma2_test.c
gcc -O3 -flto -Wall -Wextra -D_FORTIFY_SOURCE=2 -o ma2_test ma2_test.o ma2.o
ma2.o (symbol from plugin): warning: memset used with constant zero length parameter; this could be due to transposed parameters

Compiling without -flto or without -D_FORTIFY_SOURCE=2 does not give warning.
Generated code seems to work ok anyway.
Comment 1 Richard Biener 2012-01-04 10:31:09 UTC
I think this is a linker bug, GCC optimizes away the function (seeing that
the argument is _not_ zero), but the linker warns about it anyway and it
is still output for some reason:

    72: 00000000004006f0     2 FUNC    GLOBAL HIDDEN    15 __warn_memset_zero_le
n

resolution file:

2
ma2.o 4
84 cd7721f0 PREVAILING_DEF_IRONLY ma_init
95 cd7721f0 PREVAILING_DEF_IRONLY ma_pool
105 cd7721f0 PREVAILING_DEF_IRONLY ma_get_cell
124 cd7721f0 RESOLVED_EXEC __warn_memset_zero_len
ma2_test.o 4
86 64da28d6 PREVAILING_DEF main
108 64da28d6 RESOLVED_IR ma_get_cell
113 64da28d6 RESOLVED_IR ma_pool
117 64da28d6 RESOLVED_IR ma_init
Comment 2 Mark Pizzolato 2012-10-23 16:42:06 UTC
Created attachment 28514 [details]
MUCH simpler test case
Comment 3 Mark Pizzolato 2012-10-23 16:43:01 UTC
This may be a bug in gcc OR the linker.  I don't know, BUT I have a more precise description of the issue and a much simpler minimal test case.

The problem occurs when all of the following conditions are true:
    1) gcc is invoked with -O2 and -flto
    2) memset is called with a non-constant length argument
    3) memset is called with a NON zero fill value argument.

The minimal test case is:

#include <string.h>

void
main (int argc, char **argv)
{
char buf[5000];

memset (buf, 0xFF, argc);
}

To conform to the bug submission guidelines, the preprocessed file for the this test is attached.
Comment 4 Richard Biener 2014-03-21 08:58:24 UTC
typedef __SIZE_TYPE__ size_t;
extern void *memset (void *__s, int __c, size_t __n) __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1)));
extern void __warn_memset_zero_len (void) __attribute__((__warning__ ("memset used with constant zero length parameter; this could be due to transposed parameters")));
extern __inline __attribute__((__always_inline__)) __attribute__((__artificial__))
void * __attribute__ ((__nothrow__))
memset (void *__dest, int __ch, size_t __len)
{
  if (__builtin_constant_p (__len) && __len == 0
      && (!__builtin_constant_p (__ch) || __ch != 0))
    {
      __warn_memset_zero_len ();
      return __dest;
    }
  return __builtin___memset_chk (__dest, __ch, __len, 
                                 __builtin_object_size (__dest, 0));
}

void
main (int argc, char **argv)
{
  char buf[5000];

  memset (buf, 0xFF, argc);
}


This breaks a lot of applications if you build them with LTO and -D_FORTIFY_SOURCE=2.  The reason this happens is that when LTO bytecode
is output we still have

  <bb 2>:
  _2 = (long unsigned int) argc_1(D);
  _6 = __builtin_constant_p (_2);
  if (_6 != 0)
    goto <bb 3>;
  else
    goto <bb 5>;

  <bb 3>:
  if (_2 == 0)
    goto <bb 4>;
  else
    goto <bb 5>;

  <bb 4>:
  __warn_memset_zero_len ();
  goto <bb 6>;

  <bb 5>:
  __memset_chk (&buf, 255, _2, 5000);

  <bb 6>:
  buf ={v} {CLOBBER};

thus __builtin_constant_p is not yet forced to be evaluated.  This means that
we put __warn_memset_zero_len into the LTO symbol table which is queried
by the linker and this causes it to warn at the "beginning" of link-time.
Also (as can be seen with the cases where we introduce a call late) the
linker wants to see a final set of symbols at this time, thus it won't drop
the reference to __warn_memset_zero_len even if during LTRANS phase we
optimize it away.
Comment 5 Richard Biener 2014-03-21 09:00:40 UTC
It also (sadly) means this "works" with -fno-use-linker-plugin.  It also means
that not outputting the UNDEF into the LTO symbol table for this case doesn't
work as the executable will not link (we optimize the symbol away) if we don't
fold away the reference to it later.

I see no better way than either forcing the linker to re-scan needed symbols
and warn at a "second" stage only or to fold __builtin_constant_p earlier.
Comment 6 Richard Biener 2014-03-24 11:22:20 UTC
Btw, it works when using gold ...
Comment 7 Mark Pizzolato 2014-03-24 11:49:29 UTC
Thanks for reducing this test case further.  That illuminates something for me:

Specifically:

Notice in the following lines:

  if (__builtin_constant_p (__len) && __len == 0
      && (!__builtin_constant_p (__ch) || __ch != 0))
    {

The part of the expression "|| __ch != 0)"

What could possibly be the purpose of this check?

That is exactly why the 3rd condition I previously mentioned:

> 3) memset is called with a NON zero fill value argument.

triggers this issue.  The warning is about the length, but it is checking the value being set.  How could that make sense?  Clearly this check lets the vast majority of uses of memset pass without issue since all of those cases use 0 as the value to be set.

From my point of view, if that check wasn't there (i.e. remove "|| __ch != 0" from the expression), the problem would not exist.
Comment 8 Richard Biener 2014-03-24 12:14:30 UTC
(In reply to Mark Pizzolato from comment #7)
> Thanks for reducing this test case further.  That illuminates something for
> me:
> 
> Specifically:
> 
> Notice in the following lines:
> 
>   if (__builtin_constant_p (__len) && __len == 0
>       && (!__builtin_constant_p (__ch) || __ch != 0))
>     {
> 
> The part of the expression "|| __ch != 0)"
> 
> What could possibly be the purpose of this check?
> 
> That is exactly why the 3rd condition I previously mentioned:
> 
> > 3) memset is called with a NON zero fill value argument.
> 
> triggers this issue.  The warning is about the length, but it is checking
> the value being set.  How could that make sense?  Clearly this check lets
> the vast majority of uses of memset pass without issue since all of those
> cases use 0 as the value to be set.
> 
> From my point of view, if that check wasn't there (i.e. remove "|| __ch !=
> 0" from the expression), the problem would not exist.

When val == 0 then the case of len == 0 is ambiguous and you can't really
tell the user they swapped val and len (because they are equal).
Comment 9 Mark Pizzolato 2014-03-24 12:32:25 UTC
> When val == 0 then the case of len == 0 is ambiguous and you can't really
tell the user they swapped val and len (because they are equal).

That is certainly true.

I'm not sure how that specifically relates to the problem since the warning message is only about the length being 0.

The problem is that the warning is being issued incorrectly when the set value is != 0 and the length is not a constant.
Comment 10 Richard Biener 2014-03-24 13:55:20 UTC
It's a GNU ld bug I believe.

https://sourceware.org/bugzilla/show_bug.cgi?id=16746
Comment 11 Alan Modra 2014-08-07 01:05:09 UTC
This has now been fixed on mainline binutils