-
Notifications
You must be signed in to change notification settings - Fork 968
fuzz-tests: add test for amount-{sat, msat} arithmetic #8298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Hey @morehouse , running this test throws the following errors:
among a few others, which are probably due to faulty harness logic. The above failures happen in the following two functions: Has the fuzzer found an actual bug or is it just another impossible edge case? |
I think technically these are bugs -- In practice, all current usages of these functions in the codebase have positive values for |
Makes me wonder why |
Probably because there's no way to represent an unsigned float. |
2e97725 to
108f7a6
Compare
|
Hey @morehouse, As we discussed over email, I spent some time last week investigating the potential bug caused by an assertion failure in To test this path, I added a user-level Python test in In summary, it doesn’t appear that any external message can trigger the failure seen in the fuzz test. I’ve pushed the test in case you’d like to take a look. |
c587612 to
e29abd8
Compare
e29abd8 to
5dfa115
Compare
|
The test works without any breakage now and is ready to be merged. |
morehouse
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am hesitant to merge this fuzz target, since it basically has to reimplement all the operations in common/amount.c so we have something to compare against. It seems a bit silly when we could just directly review the implementations in common/amount.c.
That said, the target did find a couple cases of undefined behavior, which makes me think it would be worth merging this target at least as regression tests for those bugs.
tests/fuzz/fuzz-amount-arith.c
Outdated
| static struct amount_sat fromwire_amount_sat_bounded(const u8 **cursor, size_t *max) | ||
| { | ||
| struct amount_sat amt = fromwire_amount_sat(cursor, max); | ||
| amt.satoshis %= (MAX_BTC + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: the whole file needs to be clang-format-ed. There's a mix of indentation styles going on.
tests/fuzz/fuzz-amount-arith.c
Outdated
| struct amount_msat amt; | ||
| struct amount_sat amt_bounded = fromwire_amount_sat_bounded(cursor, max); | ||
| if (!amount_sat_to_msat(&amt, amt_bounded)) | ||
| assert(false && "amount_sat_to_msat failed!"); | ||
| return amt; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will always return amts that are multiples of 1000. Ideally we would test the full precision.
tests/fuzz/fuzz-amount-arith.c
Outdated
| struct amount_sat sb = amount_msat_to_sat_round_down(b); | ||
|
|
||
| u64 u64_param; | ||
| memcpy(&u64_param, &f, sizeof(u64_param)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems odd. Why are we copying the double into a u64?
Since we have all these different params we might use, maybe we should just put them in a struct for simplicity:
struct fuzzing_params {
enum op op;
struct amount_msat a;
struct amount_msat b;
double f;
u64 u64_param;
};
void run(const u8 *data, size_t size) {
struct fuzzing_params f;
if (size < sizeof(f)) return;
memcpy(&f, data, sizeof(f));
...
}
tests/fuzz/fuzz-amount-arith.c
Outdated
| static struct amount_sat fromwire_amount_sat_bounded(const u8 **cursor, size_t *max) | ||
| { | ||
| struct amount_sat amt = fromwire_amount_sat(cursor, max); | ||
| amt.satoshis %= (MAX_BTC + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By limiting sats in this way, we will probably miss the overflow code paths during fuzzing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but isn't it a valid assumption that in production we won't be doing arithmetic on anything greater than the greatest possible value for satoshis/millisatoshis?
tests/fuzz/fuzz-amount-arith.c
Outdated
| /* Guard against NaN and -ve factors. */ | ||
| if (f != f || f < 0) | ||
| break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is here to avoid the UBSan error that is fixed in: #8306. Same for OP_SAT_SCALE.
I think it would be better to merge that PR first and remove this guard here.
tests/fuzz/fuzz-amount-arith.c
Outdated
| struct amount_msat expected_total; | ||
| assert(amount_msat_add(&expected_total, original, fee)); | ||
| assert(amount_msat_eq(total, expected_total)); | ||
|
|
||
| a = total; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this is testing that original + fee == original + fee, which will always be true. I think this whole case is probably pointless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this check is redundant but do think there is some merit to the test case, it at least makes sure amount_msat_fee doesn't crash with arbitrary input.
tests/fuzz/fuzz-amount-arith.c
Outdated
| if (amount_msat_fee(&fee, output, fee_base, fee_prop)) { | ||
| struct amount_msat sum; | ||
| if (amount_msat_add(&sum, output, fee)) | ||
| assert(amount_msat_less_eq(sum, input)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should they be exactly equal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In amount_msat_fee's defintion:
/* BOLT #7:
*
* - SHOULD accept HTLCs that pay a fee equal to or greater than:
* - fee_base_msat + ( amount_to_forward * fee_proportional_millionths / 1000000 )
*/
So it is valid for sum to be <= input.
tests/fuzz/fuzz-amount-arith.c
Outdated
| if (b.millisatoshis > SIZE_MAX) | ||
| break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check seems pointless, since the cast to size_t below will ensure that weight < SIZE_MAX.
tests/fuzz/fuzz-amount-arith.c
Outdated
| { | ||
| if (b.millisatoshis > SIZE_MAX) | ||
| break; | ||
| u32 fee_per_kw = (u32)(a.millisatoshis & UINT32_MAX); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Masking is pointless if we're casting to u32 anyway.
tests/fuzz/fuzz-amount-arith.c
Outdated
| /* weights > 2^32 are not real tx and hence, discarded */ | ||
| if (mul_overflows_u64(fee_per_kw, weight)) | ||
| break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should just mask the weight with UINT32_MAX and remove this check.
Changelog-None: The `fuzz-amount` test doesn't fuzz the arithmetic operations for `struct amount_sat` and `struct amount_msat`. Add a test for them.
Add a minimal input set as a seed corpus for the newly introduced test. This leads to discovery of interesting code paths faster.
5dfa115 to
5fdb42c
Compare
|
The corpus for this test is now generated with the fix in #8306 applied, so that PR needs to be merged before this one can. |
The fuzz-amount test doesn't fuzz the arithmetic operations for
struct amount_satandstruct amount_msat. Add a test for the same.Checklist
Before submitting the PR, ensure the following tasks are completed. If an item is not applicable to your PR, please mark it as checked: