[CC += linux-man@, since we're discussing an API documented there, and the manual page would also need to be updated] Hi Xi, Jakub, On Fri, Jul 05, 2024 at 09:38:21PM GMT, Xi Ruoyao wrote: > On Fri, 2024-07-05 at 15:03 +0200, Alejandro Colomar wrote: > > ISO C specifies these APIs as accepting a restricted pointer in their > > first parameter: > > > > $ stdc c99 strtol > > long int strtol(const char *restrict nptr, char **restrict endptr, int base); > > $ stdc c11 strtol > > long int strtol(const char *restrict nptr, char **restrict endptr, int base); > > > > However, it should be considered a defect in ISO C. It's common to see > > code that aliases it: > > > > char str[] = "10 20"; > > > > p = str; > > a = strtol(p, &p, 0); // Let's ignore error handling for > > b = strtol(p, &p, 0); // simplicity. > > Why this is wrong? > > During the execution of strtol() the only expression accessing the > object "p" is *endptr. When the body of strtol() refers "nptr" it > accesses a different object, not "p". <http://port70.net/~nsz/c/c11/n1570.html#6.7.3p8> Theoretically, 'restrict' is defined in terms of accesses, not just references, so it's fine for strtol(3) to hold two references of p in restrict pointers. That is, the following code is valid: int dumb(int *restrict a, int *restrict also_a) { // We don't access the objects return a == also_a; } int main(void) { int x = 3; return dumb(&x, &x); } However, in practice that's dumb. The caller cannot know that the function doesn't access the object, so it must be cautious and enable -Wrestrict, which should be paranoid and do not allow passing references to the same object in different arguments, just in case the function decides to access to objects. Of course, GCC reports a diagnostic for the previous code: $ cc -Wall -Wextra dumb.c dumb.c: In function ‘main’: dumb.c:13:21: warning: passing argument 1 to ‘restrict’-qualified parameter aliases with argument 2 [-Wrestrict] 13 | return dumb(&x, &x); | ^~ ~~ ... even when there's no UB, since the object is not being accessed. But when the thing gets non-trivial, as in strtol(3), GCC misses the -Wrestrict diagnostic, as reported in <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112833>. Let's write a reproducer by altering the dumb.c program from above, with just another reference: int dumb2(int *restrict a, int *restrict *restrict ap) { // We don't access the objects return a == *ap; } int main(void) { int x = 3; int *xp = &x; return dumb2(&x, &xp); } GCC doesn't report anything bad here, even though it's basically the same as the program from above: $ cc -Wall -Wextra dumb2.c $ Again, there's no UB, but we really want to be cautious and get a diagnostic as callers, just in case the callee decides to access the object; we never know. So, GCC should be patched to report a warning in the program above. That will also cause strtol(3) to start issuing warnings in use cases like the one I showed. Even further, let's try something really weird: inequality comparison, which is only defined for pointers to the same array object: int dumb3(int *restrict a, int *restrict *restrict ap) { // We don't access the objects return a > *ap; } int main(void) { int x = 3; int *xp = &x; return dumb3(&x, &xp); } The behavior is still defined, since the obnjects are not accessed, but the compiler should really warn, on both sides: - The caller is passing references to the same object in restricted parameters, which is a red flag. - The callee is comparing for inequality pointers that should, under normal circumstances, cause Undefined Behavior. > And if this is really wrong you should report it to WG14 before changing > glibc. Well, I don't know how to report that defect to WG14. If you help me, I'll be pleased to do so. Do they have a public mailing list or anything like that? Cheers, Alex -- <https://www.alejandro-colomar.es/>
Attachment:
signature.asc
Description: PGP signature