The config_parse_units function uses 64-bit arithmetic, but does not detect 64-bit overflow. This means that values like "20000000 TB", which should be rejected, are instead mis-parsed.
Since this function is only used for configuration parsing, it's not a huge issue, but it should be simple enough to resolve this.
Found while working on 30893.
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Child items 0
Show closed items
No child items are currently assigned. Use child items to break down this issue into smaller parts.
Linked items 0
Link issues together to show that they're related.
Learn more.
Hi teor, #30864 (moved) has been merged and I have a couple of questions.
Nick added a couple of commented out tests in which it is expected for config_parse_memunit (and thus config_parse_units) to return 0 instead of UINT64_MAX when detecting 64-bit overflow.
Is this the expected behavior, or should it fallback to UINT64_MAX?
I think that's a better behavior for config_parse_memunit(). In the case of config_parse_memunit(), it's better that the user be told about the problem than to have us silently ignore their request.
Should lib/intmath/*.h be appended to confmgt .may_include?
Yes, that's fine. The purpose of .may_include is to make sure that low-level modules don't include headers from high-level modules, and lib/intmath is a nice low-level module.
Is this the expected behavior, or should it fallback to UINT64_MAX?
I think that's a better behavior for config_parse_memunit(). In the case of config_parse_memunit(), it's better that the user be told about the problem than to have us silently ignore their request.
Understood, in that case is it ok to detect overflow inside config_parse_memunit() by check if nowrap_multiplication yielded UINTMAX_64? It seems quite strange in my opinion to specify such value explicitly.
In addition to that I'm not sure if float multiplication should be handled as well or this ticket should just focus on unsigned integer.
Is this the expected behavior, or should it fallback to UINT64_MAX?
I think that's a better behavior for config_parse_memunit(). In the case of config_parse_memunit(), it's better that the user be told about the problem than to have us silently ignore their request.
Understood, in that case is it ok to detect overflow inside config_parse_memunit() by check if nowrap_multiplication yielded UINTMAX_64? It seems quite strange in my opinion to specify such value explicitly.
Maybe we should fail on anything larger than SSIZE_T_MAX?
(SSIZE_T_MAX is half the maximum possible memory size.)
In addition to that I'm not sure if float multiplication should be handled as well or this ticket should just focus on unsigned integer.
No, we don't have any critical code that uses float multiplication.
You're right, SSIZE_MAX is 2^31^ on 32-bit platforms, which would be a valid value for bandwidth on 32-bit machines. And memory on machines that allow processes to take up more than 2 GB.
Let's check that the value is less than INT64_MAX?
And let's check the result of the float multiplication, before we cast it to a uint64_t.
(We want to use a value that's significantly lower than UINT64_MAX, so that floating point calculations can't change the result.)
Let's check that the value is less than INT64_MAX?
And let's check the result of the float multiplication, before we cast it to a uint64_t.
(We want to use a value that's significantly lower than UINT64_MAX, so that floating point calculations can't change the result.)
Not sure if I got it right, waiting for an OK because I ended up writing the same block for the float as the uint case.
Just missing a floating point check, and some floating-point tests.
Thank you for your suggestions Teor.
Updated with a couple floating point tests. But probably not ok, see edit.
Edit: I think I will need some more time to read about integer representation of floating point numbers in order to understand why I'm choosing a particular number for the test and avoid just trying out numbers.
I've updated the PR with a couple of tests, one that fails the double check and one that passes double but fails int check.
Reasoning:
// Fails double check //
15000000.5 TB fails double check as its a greater floating number than (double)INT64_MAX
// Passes double check but fails int check //
8388608.1 TB passes double check because it falls in the same value as (double)INT64_MAX (which is 2^63^), but will fail the int check because (uint64_t)d, which is 2^63^, is strictly greater than 2^63^-1 (INT64_MAX)
Looks good to me too. It just needs some documentation updates and (I think) a sign check.
Which documentation specifically? torrc options in the man page?
About the sign check (if I understand correctly, checking if possitive), is this because tor_parse_double does not indeed use the min parameter?
Wouldn't the bit sign for negative numbers end up giving a uint representation greater than INT64_MAX thus failing the uint check?