Bug 89699 - [8/9 Regression] false warning -Wstringop-overflow and memcmp
Summary: [8/9 Regression] false warning -Wstringop-overflow and memcmp
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 8.3.1
: P3 normal
Target Milestone: 8.4
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks: Wstringop-overflow
  Show dependency treegraph
 
Reported: 2019-03-13 11:23 UTC by Xavier
Modified: 2019-03-13 16:38 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2019-03-13 00:00:00


Attachments
reduced test case (128 bytes, text/x-csrc)
2019-03-13 11:23 UTC, Xavier
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Xavier 2019-03-13 11:23:06 UTC
Created attachment 45958 [details]
reduced test case

I am using gcc 8.3.1 20190228

I got a strange warning on a memcmp call, it looks like gcc is tricked to believe the length is negative and that it overflows, or something like that ? Not sure.

With "gcc -O -c memcmp-short.c" :

memcmp-short.c: In function ‘f’:
memcmp-short.c:9:12: warning: ‘memcmp’ specified size between 18446744071562067968 and 18446744073709551615 exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=]
     return memcmp(s1, s2, min);
            ^~~~~~~~~~~~~~~~~~~

This is only triggered with optimizations (-O).
And only with gcc >= 8 according to godbolt.

I think I will use a workaround similar to the one suggested in 89689 :
if (min < 0) { __builtin_unreachable(); }

Cheers !
Comment 1 Jakub Jelinek 2019-03-13 13:55:47 UTC
Warning like this shouldn't be enabled by default with the amount of false positives it has.

In this case, we have:
  # RANGE [-2147483648, 0]
  min_6 = MIN_EXPR <len1_5, 0>;
  # RANGE ~[1, 18446744071562067967]
  _3 = (long unsigned int) min_6;
  _7 = __builtin_memcmp (s1.0_1, "", _3);
where we don't know anything about len1 (VARYING), but as we folded strlen ("") into 0, it is min is actually MIN (len1, 0) and so the range of it is indeed [-2147483648, 0] and that converted to size_t is 0 or -2147483648UL or higher.

As a workaround, I'd strongly recommend not using int variables to hold sizes of strings, it should be using size_t instead.
Comment 2 Martin Sebor 2019-03-13 16:38:38 UTC
The warning here is by design.  When the value's range is negative and includes zero it's taken to be strictly negative because it's so much more likely.  We could adjust this heuristic if we thought it necessary (simply by passing allow_zero = true to get_size_range()) but since as Jakub said the size would more appropriately be represented as unsigned (otherwise a large strlen result could result in min being negative), I don't believe it is.  Thus this is not a false positive.

The following is a simplified version of the test case.

$ cat z.c && gcc -O2 -S -Wall -fdump-tree-vrp1=/dev/stdout z.c
int f (const char *s1, const char *s2, int i)
{
  int min = i < 0 ? i : 0;
  return __builtin_memcmp (s1, s2, min);
}

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

;; 1 loops found
;;
;; Loop 0
;;  header 0, latch 1
;;  depth 0, outer -1
;;  nodes: 0 1 2
;; 2 succs { 1 }

Value ranges after VRP:

_1: long unsigned int ~[1, 18446744071562067967]
i_2(D): VARYING
min_3: int [-INF, 0]
s1_5(D): VARYING
s2_6(D): VARYING
_7: VARYING


f (const char * s1, const char * s2, int i)
{
  int min;
  long unsigned int _1;
  int _7;

  <bb 2> [local count: 1073741824]:
  min_3 = MIN_EXPR <i_2(D), 0>;
  _1 = (long unsigned int) min_3;
  _7 = __builtin_memcmp (s1_5(D), s2_6(D), _1);
  return _7;

}


z.c: In function ‘f’:
z.c:4:10: warning: ‘__builtin_memcmp’ specified size between 18446744071562067968 and 18446744073709551615 exceeds maximum object size 9223372036854775807 [-Wstringop-overflow=]
    4 |   return __builtin_memcmp (s1, s2, min);
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~