[Bug c/81801] New: [PATCH] Difference of two pointers generates signed overflow
oss at malat dot biz
gcc-bugzilla@gcc.gnu.org
Thu Aug 10 15:15:00 GMT 2017
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.
More information about the Gcc-bugs
mailing list