Skip to content

Commit 5abb0f5

Browse files
committed
Do base type propagation in miniscript_stable fuzzer
Keep track of which base type (B, K, V, or W) is desired in the miniscript_stable ConsumeStableNode function. This allows aborting early if the constructed node won't have the right type. Note that this does not change the fuzzer format; the meaning of inputs in ConsumeStableNode is unmodified. The only change is that often the fuzzer will abort early. The direct motivation is preventing recursing v: wrappers, which are the only fragment type that does not otherwise increase the overall minimum possible script size. In a later commit this will be exploited to prevent overly-large scripts from being constructed.
1 parent 519ec26 commit 5abb0f5

File tree

1 file changed

+85
-28
lines changed

1 file changed

+85
-28
lines changed

src/test/fuzz/miniscript.cpp

Lines changed: 85 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -329,54 +329,111 @@ std::optional<uint32_t> ConsumeTimeLock(FuzzedDataProvider& provider) {
329329
* bytes as the number of keys define the index of each key in the test data.
330330
* - For thresh(), the next byte defines the threshold value and the following one the number of subs.
331331
*/
332-
std::optional<NodeInfo> ConsumeNodeStable(FuzzedDataProvider& provider) {
332+
std::optional<NodeInfo> ConsumeNodeStable(FuzzedDataProvider& provider, Type type_needed) {
333+
bool allow_B = (type_needed == ""_mst) || (type_needed << "B"_mst);
334+
bool allow_K = (type_needed == ""_mst) || (type_needed << "K"_mst);
335+
bool allow_V = (type_needed == ""_mst) || (type_needed << "V"_mst);
336+
bool allow_W = (type_needed == ""_mst) || (type_needed << "W"_mst);
337+
333338
switch (provider.ConsumeIntegral<uint8_t>()) {
334-
case 0: return {{Fragment::JUST_0}};
335-
case 1: return {{Fragment::JUST_1}};
336-
case 2: return {{Fragment::PK_K, ConsumePubKey(provider)}};
337-
case 3: return {{Fragment::PK_H, ConsumePubKey(provider)}};
339+
case 0:
340+
if (!allow_B) return {};
341+
return {{Fragment::JUST_0}};
342+
case 1:
343+
if (!allow_B) return {};
344+
return {{Fragment::JUST_1}};
345+
case 2:
346+
if (!allow_K) return {};
347+
return {{Fragment::PK_K, ConsumePubKey(provider)}};
348+
case 3:
349+
if (!allow_K) return {};
350+
return {{Fragment::PK_H, ConsumePubKey(provider)}};
338351
case 4: {
352+
if (!allow_B) return {};
339353
const auto k = ConsumeTimeLock(provider);
340354
if (!k) return {};
341355
return {{Fragment::OLDER, *k}};
342356
}
343357
case 5: {
358+
if (!allow_B) return {};
344359
const auto k = ConsumeTimeLock(provider);
345360
if (!k) return {};
346361
return {{Fragment::AFTER, *k}};
347362
}
348-
case 6: return {{Fragment::SHA256, ConsumeSha256(provider)}};
349-
case 7: return {{Fragment::HASH256, ConsumeHash256(provider)}};
350-
case 8: return {{Fragment::RIPEMD160, ConsumeRipemd160(provider)}};
351-
case 9: return {{Fragment::HASH160, ConsumeHash160(provider)}};
363+
case 6:
364+
if (!allow_B) return {};
365+
return {{Fragment::SHA256, ConsumeSha256(provider)}};
366+
case 7:
367+
if (!allow_B) return {};
368+
return {{Fragment::HASH256, ConsumeHash256(provider)}};
369+
case 8:
370+
if (!allow_B) return {};
371+
return {{Fragment::RIPEMD160, ConsumeRipemd160(provider)}};
372+
case 9:
373+
if (!allow_B) return {};
374+
return {{Fragment::HASH160, ConsumeHash160(provider)}};
352375
case 10: {
376+
if (!allow_B) return {};
353377
const auto k = provider.ConsumeIntegral<uint8_t>();
354378
const auto n_keys = provider.ConsumeIntegral<uint8_t>();
355379
if (n_keys > 20 || k == 0 || k > n_keys) return {};
356380
std::vector<CPubKey> keys{n_keys};
357381
for (auto& key: keys) key = ConsumePubKey(provider);
358382
return {{Fragment::MULTI, k, std::move(keys)}};
359383
}
360-
case 11: return {{3, Fragment::ANDOR}};
361-
case 12: return {{2, Fragment::AND_V}};
362-
case 13: return {{2, Fragment::AND_B}};
363-
case 15: return {{2, Fragment::OR_B}};
364-
case 16: return {{2, Fragment::OR_C}};
365-
case 17: return {{2, Fragment::OR_D}};
366-
case 18: return {{2, Fragment::OR_I}};
384+
case 11:
385+
if (!(allow_B || allow_K || allow_V)) return {};
386+
return {{{"B"_mst, type_needed, type_needed}, Fragment::ANDOR}};
387+
case 12:
388+
if (!(allow_B || allow_K || allow_V)) return {};
389+
return {{{"V"_mst, type_needed}, Fragment::AND_V}};
390+
case 13:
391+
if (!allow_B) return {};
392+
return {{{"B"_mst, "W"_mst}, Fragment::AND_B}};
393+
case 15:
394+
if (!allow_B) return {};
395+
return {{{"B"_mst, "W"_mst}, Fragment::OR_B}};
396+
case 16:
397+
if (!allow_V) return {};
398+
return {{{"B"_mst, "V"_mst}, Fragment::OR_C}};
399+
case 17:
400+
if (!allow_B) return {};
401+
return {{{"B"_mst, "B"_mst}, Fragment::OR_D}};
402+
case 18:
403+
if (!(allow_B || allow_K || allow_V)) return {};
404+
return {{{type_needed, type_needed}, Fragment::OR_I}};
367405
case 19: {
406+
if (!allow_B) return {};
368407
auto k = provider.ConsumeIntegral<uint8_t>();
369408
auto n_subs = provider.ConsumeIntegral<uint8_t>();
370409
if (k == 0 || k > n_subs) return {};
371-
return {{n_subs, Fragment::THRESH, k}};
410+
std::vector<Type> subtypes;
411+
subtypes.reserve(n_subs);
412+
subtypes.emplace_back("B"_mst);
413+
for (size_t i = 1; i < n_subs; ++i) subtypes.emplace_back("W"_mst);
414+
return {{std::move(subtypes), Fragment::THRESH, k}};
372415
}
373-
case 20: return {{1, Fragment::WRAP_A}};
374-
case 21: return {{1, Fragment::WRAP_S}};
375-
case 22: return {{1, Fragment::WRAP_C}};
376-
case 23: return {{1, Fragment::WRAP_D}};
377-
case 24: return {{1, Fragment::WRAP_V}};
378-
case 25: return {{1, Fragment::WRAP_J}};
379-
case 26: return {{1, Fragment::WRAP_N}};
416+
case 20:
417+
if (!allow_W) return {};
418+
return {{{"B"_mst}, Fragment::WRAP_A}};
419+
case 21:
420+
if (!allow_W) return {};
421+
return {{{"B"_mst}, Fragment::WRAP_S}};
422+
case 22:
423+
if (!allow_B) return {};
424+
return {{{"K"_mst}, Fragment::WRAP_C}};
425+
case 23:
426+
if (!allow_B) return {};
427+
return {{{"V"_mst}, Fragment::WRAP_D}};
428+
case 24:
429+
if (!allow_V) return {};
430+
return {{{"B"_mst}, Fragment::WRAP_V}};
431+
case 25:
432+
if (!allow_B) return {};
433+
return {{{"B"_mst}, Fragment::WRAP_J}};
434+
case 26:
435+
if (!allow_B) return {};
436+
return {{{"B"_mst}, Fragment::WRAP_N}};
380437
default:
381438
break;
382439
}
@@ -709,7 +766,7 @@ std::optional<NodeInfo> ConsumeNodeSmart(FuzzedDataProvider& provider, Type type
709766
* a NodeRef whose Type() matches the type fed to ConsumeNode.
710767
*/
711768
template<typename F>
712-
NodeRef GenNode(F ConsumeNode, Type root_type = ""_mst, bool strict_valid = false) {
769+
NodeRef GenNode(F ConsumeNode, Type root_type, bool strict_valid = false) {
713770
/** A stack of miniscript Nodes being built up. */
714771
std::vector<NodeRef> stack;
715772
/** The queue of instructions. */
@@ -921,9 +978,9 @@ void FuzzInitSmart()
921978
FUZZ_TARGET_INIT(miniscript_stable, FuzzInit)
922979
{
923980
FuzzedDataProvider provider(buffer.data(), buffer.size());
924-
TestNode(GenNode([&](Type) {
925-
return ConsumeNodeStable(provider);
926-
}), provider);
981+
TestNode(GenNode([&](Type needed_type) {
982+
return ConsumeNodeStable(provider, needed_type);
983+
}, ""_mst), provider);
927984
}
928985

929986
/** Fuzz target that runs TestNode on nodes generated using ConsumeNodeSmart. */

0 commit comments

Comments
 (0)