Skip to content

Commit fffd8dc

Browse files
committed
Add asmap sanity checker
1 parent 5feefbe commit fffd8dc

File tree

5 files changed

+70
-0
lines changed

5 files changed

+70
-0
lines changed

src/addrman.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,5 +644,9 @@ std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
644644
bits.push_back((cur_byte >> bit) & 1);
645645
}
646646
}
647+
if (!SanityCheckASMap(bits)) {
648+
LogPrintf("Sanity check of asmap file %s failed\n", path);
649+
return {};
650+
}
647651
return bits;
648652
}

src/netaddress.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,3 +894,8 @@ bool operator<(const CSubNet& a, const CSubNet& b)
894894
{
895895
return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0));
896896
}
897+
898+
bool SanityCheckASMap(const std::vector<bool>& asmap)
899+
{
900+
return SanityCheckASMap(asmap, 128); // For IP address lookups, the input is 128 bits
901+
}

src/netaddress.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,6 @@ class CService : public CNetAddr
180180
}
181181
};
182182

183+
bool SanityCheckASMap(const std::vector<bool>& asmap);
184+
183185
#endif // BITCOIN_NETADDRESS_H

src/util/asmap.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5+
#include <map>
56
#include <vector>
67
#include <assert.h>
78
#include <crypto/common.h>
@@ -118,3 +119,56 @@ uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip)
118119
// Reached EOF without RETURN, or aborted (see any of the breaks above).
119120
return 0; // 0 is not a valid ASN
120121
}
122+
123+
bool SanityCheckASMap(const std::vector<bool>& asmap, int bits)
124+
{
125+
const std::vector<bool>::const_iterator begin = asmap.begin(), endpos = asmap.end();
126+
std::vector<bool>::const_iterator pos = begin;
127+
std::vector<std::pair<uint32_t, int>> jumps; // All future positions we may jump to (bit offset in asmap -> bits to consume left)
128+
jumps.reserve(bits);
129+
while (pos != endpos) {
130+
uint32_t offset = pos - begin;
131+
if (!jumps.empty() && offset >= jumps.back().first) return false; // There was a jump into the middle of the previous instruction
132+
Instruction opcode = DecodeType(pos, endpos);
133+
if (opcode == Instruction::RETURN) {
134+
uint32_t asn = DecodeASN(pos, endpos);
135+
if (asn == INVALID) return false; // ASN straddles EOF
136+
if (jumps.empty()) {
137+
// Nothing to execute anymore
138+
if (endpos - pos > 7) return false; // Excessive padding
139+
while (pos != endpos) {
140+
if (*pos) return false; // Nonzero padding bit
141+
++pos;
142+
}
143+
return true; // Sanely reached EOF
144+
} else {
145+
// Continue by pretending we jumped to the next instruction
146+
offset = pos - begin;
147+
if (offset != jumps.back().first) return false; // Unreachable code
148+
bits = jumps.back().second; // Restore the number of bits we would have had left after this jump
149+
jumps.pop_back();
150+
}
151+
} else if (opcode == Instruction::JUMP) {
152+
uint32_t jump = DecodeJump(pos, endpos);
153+
if (jump == INVALID) return false; // Jump offset straddles EOF
154+
if (jump > endpos - pos) return false; // Jump out of range
155+
if (bits == 0) return false; // Consuming bits past the end of the input
156+
--bits;
157+
uint32_t jump_offset = pos - begin + jump;
158+
if (!jumps.empty() && jump_offset >= jumps.back().first) return false; // Intersecting jumps
159+
jumps.emplace_back(jump_offset, bits);
160+
} else if (opcode == Instruction::MATCH) {
161+
uint32_t match = DecodeMatch(pos, endpos);
162+
if (match == INVALID) return false; // Match bits straddle EOF
163+
int matchlen = CountBits(match) - 1;
164+
if (bits < matchlen) return false; // Consuming bits past the end of the input
165+
bits -= matchlen;
166+
} else if (opcode == Instruction::DEFAULT) {
167+
uint32_t asn = DecodeASN(pos, endpos);
168+
if (asn == INVALID) return false; // ASN straddles EOF
169+
} else {
170+
return false; // Instruction straddles EOF
171+
}
172+
}
173+
return false; // Reached EOF without RETURN instruction
174+
}

src/util/asmap.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
#ifndef BITCOIN_UTIL_ASMAP_H
66
#define BITCOIN_UTIL_ASMAP_H
77

8+
#include <stdint.h>
9+
#include <vector>
10+
811
uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip);
912

13+
bool SanityCheckASMap(const std::vector<bool>& asmap, int bits);
14+
1015
#endif // BITCOIN_UTIL_ASMAP_H

0 commit comments

Comments
 (0)