Repository navigation
LibC: Fix strtol() and friends for "0x" prefix, signs and overflow - #27118
Ramji-Purwar wants to merge 4 commits into
Conversation
| // The remainder of a negative min_value is negative, but it is compared against (positive) digit values. | ||
| m_max_digit_after_cutoff = positive() ? (max_value % base) : -(min_value % base); |
There was a problem hiding this comment.
I think I understand what the the change wants to achieve,
but I am not sure what this has to do with LLONG_MIN specifically, as this seemed to have been an issue with all negative numbers?
TBF I have no clue what this is trying to achieve with the cutoff anyway
There was a problem hiding this comment.
Ah read the PR description a bit more, now I understand a bit more,
IMO the class likely needs a rewrite....
There was a problem hiding this comment.
Only numbers whose first digits are exactly the cutoff (LLONG_MIN / base, i.e. -922337203685477580 for base 10) reach the comparison against m_max_digit_after_cutoff. All other negative numbers are either clearly below the cutoff or already past it, so they never hit the broken check. That's why only -9223372036854775800 to -9223372036854775808 were affected.
I agree that NumParser could be simplified. I'd prefer to keep this PR a minimal fix, but I'm happy to do a cleanup in a follow-up PR, or here if you'd rather have it in this one.
There was a problem hiding this comment.
It would have been nice to have the tests in the feature commits, but this way is also fine I guess
There was a problem hiding this comment.
Thanks! I can move the tests into the commits that fix each case if you'd like, just let me know.
Fixes #27117
strtol(),strtoll(),strtoul()andstrtoull()returned wrong results for some valid inputs.Problems before this PR
strtol("0x1A", NULL, 16)026strtol("-0x10", NULL, 16)0-16strtoul("+42", NULL, 10)042strtoul("-1", NULL, 10)0ULONG_MAXstrtoull("99999999999999999999", NULL, 10)LLONG_MAXULLONG_MAXstrtoll("-9223372036854775807", NULL, 10)LLONG_MIN,errno = ERANGE-9223372036854775807This also affected programs that ship with Serenity:
printf '%u\n' +42printed0. It now prints42.0xfffilled the selection with00. It now fills it withFF.What caused the problems
strtoll()andstrtoull()each had their own copy of the code that parses the base and the0xprefix, and the two copies were different.strtoull()skipped0xwhen the base was 16, butstrtoll()only did that when the base was 0. With base 16,strtoll()parsed the0, stopped at thexand returned0.strtol()callsstrtoll(), so it had the same problem.strtoull()never parsed the optional sign, unlikestrtoll(). A leading+or-was treated as an invalid character, so no digits were read and it returned0.strtoull()overflowed, it returnedLONG_LONG_MAX, which iype, instead ofULONG_LONG_MAX.LLONG_MIN % baseas the limit for the last digit. In C++ that remainder is negative (-8for base 10), so every last digit failed thedigit <= limitcheck and the number was reportedChanges
strtoll()andstrtoull()now share one helper for the base andstrtoll()also accepts the prefix when the base is 16. The prefixis only skipped if a hex digit follows it, so"0x"on its own parses as0, as POSIX requires.strtoull()now accepts a leading+or-(a-negates the r returnsULLONG_MAXinstead ofLLONG_MAXon overflow.strtoll()treat values from-9223372036854775800to-9223372036854775808as an overflow.Tests/LibC/TestStrtol.cpp.