We use a custom mutator to produce an input corpus that consists entirely of correctly encoded bech32 strings. This enables us to efficiently fuzz the bolt11 decoding logic without the fuzzer getting stuck on fuzzing the bech32 decoding/encoding logic. This custom mutator does the following things: 1. Attempt to bech32 decode the given input (returns the encoded dummy invoice on failure). 2. Muta
| 88 | // 4. Write the encoded result to `fuzz_data` if its size does not exceed |
| 89 | // `max_size`, otherwise return the encoded dummy invoice. |
| 90 | size_t LLVMFuzzerCustomMutator(uint8_t *fuzz_data, size_t size, size_t max_size, |
| 91 | unsigned int seed) |
| 92 | { |
| 93 | // A minimum size of 9 prevents hrp_maxlen <= 0 and data_maxlen <= 0. |
| 94 | if (size < 9) |
| 95 | return initial_input(fuzz_data, size, max_size); |
| 96 | |
| 97 | // Interpret fuzz input as string (ensure it's null terminated). |
| 98 | char *input = to_string(tmpctx, fuzz_data, size); |
| 99 | |
| 100 | // Attempt to bech32 decode the input. |
| 101 | size_t hrp_maxlen = strlen(input) - 6; |
| 102 | char *hrp = tal_arr(tmpctx, char, hrp_maxlen); |
| 103 | size_t data_maxlen = strlen(input) - 8; |
| 104 | u5 *data = tal_arr(tmpctx, u5, data_maxlen); |
| 105 | size_t datalen = 0; |
| 106 | if (bech32_decode(hrp, data, &datalen, input, (size_t)-1) != |
| 107 | BECH32_ENCODING_BECH32) { |
| 108 | // Decoding failed, this should only happen when starting from |
| 109 | // an empty corpus. |
| 110 | return initial_input(fuzz_data, size, max_size); |
| 111 | } |
| 112 | |
| 113 | // Mutate either the hrp or data. Given the same seed, the same |
| 114 | // mutation is performed. |
| 115 | srand(seed); |
| 116 | switch (rand() % 2) { |
| 117 | case 0: { // Mutate hrp and ensure it's still null terminated. |
| 118 | size_t new_len = LLVMFuzzerMutate((uint8_t *)hrp, strlen(hrp), |
| 119 | hrp_maxlen - 1); |
| 120 | hrp[new_len] = '\0'; |
| 121 | break; |
| 122 | } |
| 123 | case 1: // Mutate data and re-assign datalen. |
| 124 | datalen = |
| 125 | LLVMFuzzerMutate((uint8_t *)data, datalen, data_maxlen); |
| 126 | break; |
| 127 | } |
| 128 | |
| 129 | // Encode the mutated input. |
| 130 | char *output = tal_arr(tmpctx, char, strlen(hrp) + datalen + 8); |
| 131 | if (!bech32_encode(output, hrp, data, datalen, (size_t)-1, |
| 132 | BECH32_ENCODING_BECH32)) { |
| 133 | return initial_input(fuzz_data, size, max_size); |
| 134 | } |
| 135 | |
| 136 | // Write the result into `fuzz_data`. |
| 137 | size_t output_len = strlen(output); |
| 138 | if (output_len > max_size) |
| 139 | return initial_input(fuzz_data, size, max_size); |
| 140 | |
| 141 | memcpy(fuzz_data, output, output_len); |
| 142 | clean_tmpctx(); |
| 143 | return output_len; |
| 144 | } |
| 145 | |
| 146 | size_t LLVMFuzzerCustomCrossOver(const u8 *in1, size_t in1_size, const u8 *in2, |
| 147 | size_t in2_size, u8 *out, size_t max_out_size, |
nothing calls this directly
no test coverage detected