Skip to content

Commit cf4da5e

Browse files
committed
Merge bitcoin/bitcoin#28015: fuzz: Generate rpc fuzz targets individually
fa1e27f fuzz: Generate rpc fuzz targets individually (MarcoFalke) Pull request description: The `rpc` fuzz target was added more than two years ago in e458631. However, the bug bitcoin/bitcoin#27913 was only found recently. Thus, it is pretty clear that fuzz engines can't deal with a search space that is too broad and can be extended in too many directions. Fix that by limiting the search space to each RPC method name and then iterate over all names, instead of letting the fuzz engine do the iteration. With this, the bug can be found in seconds, as opposed to years of CPU time (or never). ACKs for top commit: brunoerg: ACK fa1e27f dergoegge: ACK fa1e27f Tree-SHA512: 45ccba842367650d010320603153276b1b303deda9ba8c6bb31a4d2473b00aa5bca866db95f541485d65efd8276e2575026968c037872ef344fa33cf45bcdcd7
2 parents a9dde84 + fa1e27f commit cf4da5e

File tree

1 file changed

+26
-8
lines changed

1 file changed

+26
-8
lines changed

test/fuzz/test_runner.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,29 +200,47 @@ def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
200200
{corpus_dir}.
201201
"""
202202
logging.info("Generating corpus to {}".format(corpus_dir))
203+
rpc_target = "rpc"
204+
has_rpc = rpc_target in targets
205+
if has_rpc:
206+
targets.remove(rpc_target)
207+
targets = [(t, {}) for t in targets]
208+
if has_rpc:
209+
lines = subprocess.run(
210+
["git", "grep", "--function-context", "RPC_COMMANDS_SAFE_FOR_FUZZING{", os.path.join(src_dir, "src", "test", "fuzz", "rpc.cpp")],
211+
check=True,
212+
stdout=subprocess.PIPE,
213+
text=True,
214+
).stdout.splitlines()
215+
lines = [l.split("\"", 1)[1].split("\"")[0] for l in lines if l.startswith("src/test/fuzz/rpc.cpp- \"")]
216+
targets += [(rpc_target, {"LIMIT_TO_RPC_COMMAND": r}) for r in lines]
203217

204-
def job(command, t):
205-
logging.debug("Running '{}'\n".format(" ".join(command)))
218+
def job(command, t, t_env):
219+
logging.debug(f"Running '{command}'")
206220
logging.debug("Command '{}' output:\n'{}'\n".format(
207-
' '.join(command),
221+
command,
208222
subprocess.run(
209223
command,
210-
env=get_fuzz_env(target=t, source_dir=src_dir),
224+
env={
225+
**t_env,
226+
**get_fuzz_env(target=t, source_dir=src_dir),
227+
},
211228
check=True,
212229
stderr=subprocess.PIPE,
213230
text=True,
214-
).stderr))
231+
).stderr,
232+
))
215233

216234
futures = []
217-
for target in targets:
218-
target_corpus_dir = os.path.join(corpus_dir, target)
235+
for target, t_env in targets:
236+
target_corpus_dir = corpus_dir / target
219237
os.makedirs(target_corpus_dir, exist_ok=True)
220238
command = [
221239
os.path.join(build_dir, 'src', 'test', 'fuzz', 'fuzz'),
222240
"-runs=100000",
223241
target_corpus_dir,
224242
]
225-
futures.append(fuzz_pool.submit(job, command, target))
243+
futures.append(fuzz_pool.submit(job, command, target, t_env))
226244

227245
for future in as_completed(futures):
228246
future.result()

0 commit comments

Comments
 (0)