refactor(miniscript): Destroy nodes one full subs-vector at a time
What changed, and why it matters
This commit rewrites how a data structure in Bitcoin Core's Miniscript code is torn down when no longer needed. The change is described as a non-functional refactor to avoid a potential stack overflow during destruction of deeply nested Miniscript expressions. There is no direct evidence in the commit that this fixes an exploitable vulnerability, and the change is not labeled as a security fix by the project.
Treat as a routine hardening/refactor commit. No urgent action is indicated unless an independent advisory demonstrates that the prior destructor behavior was exploitable as denial-of-service.
Security signals we found
Stack-overflow mitigation in destructor
Refactor of memory/lifetime management in Miniscript parser
No security framing or CVE reference in commit message
Evidence from the diff
The patch modifies the destructor of the Miniscript Node class in src/script/miniscript.h. The previous implementation iteratively flattened one node’s children into a single vector, while the new version moves entire subexpression vectors into a queue and drains them vector-by-vector. The stated goal is to prevent recursive destructor calls from overflowing the stack on deeply nested Miniscript trees. The change is functionally equivalent for normal operation and only affects object lifetime behavior under extreme nesting.
Changed components
src/script/miniscript.hMiniscript Node destructorInspect captured patch +8 / −7
diff --git a/src/script/miniscript.h b/src/script/miniscript.h
index 0c645495..1b7e84f4 100644
--- a/src/script/miniscript.h
+++ b/src/script/miniscript.h
@@ -552,14 +552,15 @@ public:
// Destroy the subexpressions iteratively after moving out their
// subexpressions to avoid a stack-overflow due to recursive calls to
// the subs' destructors.
- while (!subs.empty()) {
- auto node = std::move(subs.back());
- subs.pop_back();
- while (!node.subs.empty()) {
- subs.push_back(std::move(node.subs.back()));
- node.subs.pop_back();
+ std::vector<std::vector<Node>> queue;
+ queue.push_back(std::move(subs));
+ do {
+ auto flattening{std::move(queue.back())};
+ queue.pop_back();
+ for (Node& n : flattening) {
+ if (!n.subs.empty()) queue.push_back(std::move(n.subs));
}
- }
+ } while (!queue.empty());
}
// NOLINTEND(misc-no-recursion)
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.