MCPcopy Create free account
hub / github.com/bitcoin/bitcoin / SanityCheckAsmap

Function SanityCheckAsmap

src/util/asmap.cpp:237–302  ·  view source on GitHub ↗

* Validates ASMap structure by simulating all possible execution paths. * Ensures well-formed bytecode, valid jumps, and proper termination. */

Source from the content-addressed store, hash-verified

235 * Ensures well-formed bytecode, valid jumps, and proper termination.
236 */
237bool SanityCheckAsmap(const std::span<const std::byte> asmap, int bits)
238{
239 size_t pos{0};
240 const size_t endpos{asmap.size() * 8};
241 std::vector<std::pair<uint32_t, int>> jumps; // All future positions we may jump to (bit offset in asmap -> bits to consume left)
242 jumps.reserve(bits);
243 Instruction prevopcode = Instruction::JUMP;
244 bool had_incomplete_match = false; // Track <8 bit matches for efficiency check
245
246 while (pos != endpos) {
247 // There was a jump into the middle of the previous instruction
248 if (!jumps.empty() && pos >= jumps.back().first) return false;
249
250 Instruction opcode = DecodeType(pos, asmap);
251 if (opcode == Instruction::RETURN) {
252 // There should not be any RETURN immediately after a DEFAULT (could be combined into just RETURN)
253 if (prevopcode == Instruction::DEFAULT) return false;
254 uint32_t asn = DecodeASN(pos, asmap);
255 if (asn == INVALID) return false; // ASN straddles EOF
256 if (jumps.empty()) {
257 // Nothing to execute anymore
258 if (endpos - pos > 7) return false; // Excessive padding
259 while (pos != endpos) {
260 if (ConsumeBitLE(pos, asmap)) return false; // Nonzero padding bit
261 }
262 return true; // Sanely reached EOF
263 } else {
264 // Continue by pretending we jumped to the next instruction
265 if (pos != jumps.back().first) return false; // Unreachable code
266 bits = jumps.back().second; // Restore the number of bits we would have had left after this jump
267 jumps.pop_back();
268 prevopcode = Instruction::JUMP;
269 }
270 } else if (opcode == Instruction::JUMP) {
271 uint32_t jump = DecodeJump(pos, asmap);
272 if (jump == INVALID) return false; // Jump offset straddles EOF
273 if (int64_t{jump} > static_cast<int64_t>(endpos - pos)) return false; // Jump out of range
274 if (bits == 0) return false; // Consuming bits past the end of the input
275 --bits;
276 uint32_t jump_offset = pos + jump;
277 if (!jumps.empty() && jump_offset >= jumps.back().first) return false; // Intersecting jumps
278 jumps.emplace_back(jump_offset, bits); // Queue jump target for validation
279 prevopcode = Instruction::JUMP;
280 } else if (opcode == Instruction::MATCH) {
281 uint32_t match = DecodeMatch(pos, asmap);
282 if (match == INVALID) return false; // Match bits straddle EOF
283 int matchlen = std::bit_width(match) - 1;
284 if (prevopcode != Instruction::MATCH) had_incomplete_match = false;
285 // Within a sequence of matches only at most one should be incomplete
286 if (matchlen < 8 && had_incomplete_match) return false;
287 had_incomplete_match = (matchlen < 8);
288 if (bits < matchlen) return false; // Consuming bits past the end of the input
289 bits -= matchlen;
290 prevopcode = Instruction::MATCH;
291 } else if (opcode == Instruction::DEFAULT) {
292 // There should not be two successive DEFAULTs (they could be combined into one)
293 if (prevopcode == Instruction::DEFAULT) return false;
294 uint32_t asn = DecodeASN(pos, asmap);

Callers 2

FUZZ_TARGETFunction · 0.85
CheckStandardAsmapFunction · 0.85

Calls 11

DecodeTypeFunction · 0.85
DecodeASNFunction · 0.85
ConsumeBitLEFunction · 0.85
DecodeJumpFunction · 0.85
DecodeMatchFunction · 0.85
sizeMethod · 0.45
reserveMethod · 0.45
emptyMethod · 0.45
backMethod · 0.45
pop_backMethod · 0.45
emplace_backMethod · 0.45

Tested by 1

FUZZ_TARGETFunction · 0.68