Hi Matthew, On Wed, Dec 06, 2023 at 03:45:19PM -0500, Matthew House wrote: > On Wed, Dec 6, 2023 at 3:18 PM Alejandro Colomar <alx@xxxxxxxxxx> wrote: > > Hi Matthew, > > > > On Wed, Dec 06, 2023 at 01:33:50PM -0500, Matthew House wrote: > > > I feel like this is rather overstating the difficulty. In practice, the > > > no-conversion condition is very commonly detected by checking whether > > > *endptr == nptr after the call. The usual idiom I see is something like: > > > > > > char *end; > > > errno = 0; > > > value = strtol(ptr, &end, 10); > > > if (end == ptr || *end != '\0' || errno == ERANGE) > > > > That test could trigger UB, if you passed an unsupported base. Of > > course, in this case you pass 10, but what if the base was a > > user-controlled variable? In such a case, nothing says what happens to > > 'end' (experimentally, I see it is not modified, so it would be left > > uninitialized); so dereferencing it, or even comparing it, would be UB. > > > > > goto err; > > > > Yeah, if you just don't care and want to handle all errors in the same > > way, and you know the base is supported, this is correct. > > The practical answer is that the base is never ultimately a user-controlled > variable. Sometimes people define wrapper functions with a variable base, > but that base is still ultimately fixed by all its callers. If you disagree > with this, I challenge you to name a single example. Agree. But then the manual shouldn't suggest that it's fine to test for EINVAL. It would be fine to test beforehand, though: errno = 0; strtol("0", NULL, base); if (errno == EINVAL) goto bad; // Now we can work with that base. ... errrno = 0; val = strtol(str, &end, base); if (end == ptr) goto nan; if (errno == ERANGE || val < min || val > max) goto bignum; if (*end != '\0') goto garbage; I think this example would be an improvement over the current page. Still, strtoi() is simpler to use in the general case: errno = 0; val1 = strtoi(str, &end, base, min, max, &err); if (err != 0 || err != ENOTSUP) goto err; val2 = strtoi(str, &end, base, min, max, &err); if (err != 0) goto err; But yeah, this is something you can pull from libbsd, or write your own, after taking into consideration the thing about EINVAL from above. Cheers, Alex -- <https://www.alejandro-colomar.es/>
Attachment:
signature.asc
Description: PGP signature