https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81801
Bug ID: 81801 Summary: [PATCH] Difference of two pointers generates signed overflow Product: gcc Version: 8.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: oss at malat dot biz Target Milestone: --- Created attachment 41964 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=41964&action=edit Correction + test SSH compiled with -ftrapv aborts in strlcpy() function, which computes a length of a string by subtracting pointer to the string from the pointer to '\0' character terminating the string. The problem arises only if the string spawns over the positive/negative boundary, for example assume a string of the length 23 on 32-bit platform located at 0x7FFFfff8: char *str = (char *)0x7FFFfff8; char *str_end = (char *)0x8000000f; then the following: int len = str_end - str; produces a signed overflow, which leads to abort() if compiled with -ftrapv. The whole issue can be easily shown by compiling the following code: int ptrdiff(char *a, char *b) { return b - a; } but more illustrative is to use a pointer to a larger object: int ptrdiff(int *a, int *b) { return b - a; } which produces the following test.c.003t.original: ;; Function ptrdiff (null) ;; enabled by -tree-original { return ((int) b - (int) a) /[ex] 4; } I have tracked this problem down to pointer_diff() function, which explicitly uses signed type - either ptrdiff_t or larger signed type matching the pointer size if needed. I assume this is not correct and the pointer difference should always use unsigned type matching the size of the pointer to make the operation well behaving even if it overflows. After changing the function to use unsigned (the patch is attached), I get the following test.c.003t.original: ;; Function ptrdiff (null) ;; enabled by -tree-original { return (int) ((unsigned int) b - (unsigned int) a) /[ex] 4; } which looks better to me. The problem with this change is that it leads to FAIL: gcc.dg/tree-ssa/cmpexactdiv-2.c scan-tree-dump-not optimized "minus_expr" where it doesn't optimize __PTRDIFF_TYPE__ l1 = b - a; __PTRDIFF_TYPE__ l2 = c - a; return l1 < l2; to return b < c; Which it could still do, but not because it knows the overflow can't occur but because if the difference of two pointers doesn't fit to __PTRDIFF_TYPE__ the behavior is undefined - so it's not possible the difference of two pointers would wrap around to a wrong sign in the correct program.