Skip to content

Conversation

darosior
Copy link
Member

Some of the logic in the miniscript module is quadratic. It only becomes an issue for very large uninteresting descriptors (like a thresh with 130k sub-fragments or a fragment with more than 60k nested j: wrappers).

This PR fixes the two types of fuzz timeouts reported by Marco in #28812 by trying to pinpoint the problematic descriptors through a simple analysis of the string, without limiting the size of the string itself. This is the same approach as was adopted for limiting the depth of derivation paths.

@DrahtBot
Copy link
Contributor

DrahtBot commented May 30, 2024

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage

For detailed information about the code coverage, see the test coverage report.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK dergoegge, stickies-v, marcofleon

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

@DrahtBot
Copy link
Contributor

🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the
documentation.

Possibly this is due to a silent merge conflict (the changes in this pull request being
incompatible with the current code in the target branch). If so, make sure to rebase on the latest
commit of the target branch.

Leave a comment here, if you need help tracking down a confusing failure.

Debug: https://github.com/bitcoin/bitcoin/runs/25599793054

@fanquake fanquake requested review from maflcko and dergoegge May 31, 2024 09:36
{
auto count{0};
// We iterate in reverse order to start counting when we encounter a colon.
for (const auto& ch: buff | std::views::reverse) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't available yet. You'll have to use a reverse iterator manually for now.

@darosior darosior force-pushed the 2405_desc_fuzz_timeouts branch 2 times, most recently from 7ff2652 to 980fc9f Compare June 6, 2024 10:44
@DrahtBot DrahtBot removed the CI failed label Jun 6, 2024
@dergoegge
Copy link
Member

#30229 was merged if you want to rebase

@darosior darosior force-pushed the 2405_desc_fuzz_timeouts branch from 980fc9f to 178a1da Compare June 27, 2024 14:37
@darosior
Copy link
Member Author

Rebased on master to take advantage of #30229.

Copy link
Member

@dergoegge dergoegge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 178a1da

Copy link
Contributor

@stickies-v stickies-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK

Copy link
Contributor

@marcofleon marcofleon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lightly tested ACK 178a1da. I tested on the three inputs brought up in #28812 and they all executed in 1ms. I can run the target for a while and see if any other timeouts come up. But looks good to me.

@DrahtBot DrahtBot requested a review from stickies-v July 11, 2024 15:16
Copy link
Contributor

@stickies-v stickies-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Light ACK 178a1da

Code LGTM and approach seems reasonable, but there may be fuzzing and miniscript nuances I'm not considering.

@darosior darosior force-pushed the 2405_desc_fuzz_timeouts branch from 178a1da to 63f62b5 Compare July 11, 2024 18:54
@darosior
Copy link
Member Author

Thank you both for the review. I addressed your comments and heavily documented the code. I think it can be helpful as it indeed relies on being familiar with the descriptor/miniscript syntax. If only to make the assumptions in the counting logic explicit, or for when we'll all have forgotten about the details of this syntax.

I also modified two small things:

  • I introduced a limit to the number of nested sub-fragments a fragment may have. Since we are pushing an element on top of a stack for every occurrence of a (, the fuzzer might just spit a -max_len (about 1M in my experience) string of (((((((... and we'd create a stack of 1 million integers. It'd waste resources and anyways 10k subs is more than enough for anything interesting.
  • I corrected HasTooManySubs to not start counting at 1 since technically : isn't a wrapper. Instead i use an optional to detect whether we are counting or not. This also made the code slightly clearer and allowed to better document it.

Copy link
Contributor

@stickies-v stickies-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good, thanks for adding more documentation. Nits can be ignored but I think HasTooManySubFrag needs some fixes wrt return statement and type narrowing before ACK.

darosior added 2 commits July 14, 2024 17:46
This target may call into logic quadratic over the number of
sub-fragments. Limit the number of sub-fragments to keep the runtime
reasonable.

Thanks to Marco Falke for reporting the fuzz timeouts with a minimized
input.
The script building logic performs a quadratic number of copies in the
number of nested wrappers in the miniscript. Limit the number of nested
wrappers to avoid fuzz timeouts.

Thanks to Marco Falke for reporting the fuzz timeouts and providing a
minimal input to reproduce.
@darosior darosior force-pushed the 2405_desc_fuzz_timeouts branch from 63f62b5 to bc34bc2 Compare July 14, 2024 15:49
Copy link
Member

@dergoegge dergoegge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK bc34bc2

Copy link
Contributor

@stickies-v stickies-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Light ACK bc34bc2

Code LGTM and approach seems reasonable, but there may be fuzzing and miniscript nuances I'm not considering.

Copy link
Contributor

@marcofleon marcofleon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review ACK bc34bc2. The added comments are useful, thanks for those. Tested on the three inputs in #28812 that caused the timeouts.

@fanquake fanquake merged commit 262260c into bitcoin:master Jul 15, 2024
@maflcko
Copy link
Member

maflcko commented Jul 17, 2024

Should the check be applied to miniscript_string, for example the miniscript_string/ae395bdc087e233d7f8e1844d4814b2c00cc9d21 input, as well?

@darosior darosior deleted the 2405_desc_fuzz_timeouts branch July 18, 2024 14:25
@darosior
Copy link
Member Author

Yes, makes sense.

@bitcoin bitcoin locked and limited conversation to collaborators Jul 18, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants