Skip to content

Commit 1eedea0

Browse files
fanquakePastaPastaPasta
authored andcommitted
Merge 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#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
1 parent 3b3169d commit 1eedea0

File tree

1 file changed

+27
-9
lines changed

1 file changed

+27
-9
lines changed

test/fuzz/test_runner.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -193,22 +193,40 @@ def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
193193
{corpus_dir}.
194194
"""
195195
logging.info("Generating corpus to {}".format(corpus_dir))
196-
197-
def job(command, t):
198-
logging.debug("Running '{}'\n".format(" ".join(command)))
196+
rpc_target = "rpc"
197+
has_rpc = rpc_target in targets
198+
if has_rpc:
199+
targets.remove(rpc_target)
200+
targets = [(t, {}) for t in targets]
201+
if has_rpc:
202+
lines = subprocess.run(
203+
["git", "grep", "--function-context", "RPC_COMMANDS_SAFE_FOR_FUZZING{", os.path.join(src_dir, "src", "test", "fuzz", "rpc.cpp")],
204+
check=True,
205+
stdout=subprocess.PIPE,
206+
text=True,
207+
).stdout.splitlines()
208+
lines = [l.split("\"", 1)[1].split("\"")[0] for l in lines if l.startswith("src/test/fuzz/rpc.cpp- \"")]
209+
targets += [(rpc_target, {"LIMIT_TO_RPC_COMMAND": r}) for r in lines]
210+
211+
def job(command, t, t_env):
212+
logging.debug(f"Running '{command}'")
199213
logging.debug("Command '{}' output:\n'{}'\n".format(
200-
' '.join(command),
214+
command,
201215
subprocess.run(
202216
command,
203-
env=get_fuzz_env(target=t, source_dir=src_dir),
217+
env={
218+
**t_env,
219+
**get_fuzz_env(target=t, source_dir=src_dir),
220+
},
204221
check=True,
205222
stderr=subprocess.PIPE,
206223
universal_newlines=True,
207-
).stderr))
224+
).stderr,
225+
))
208226

209227
futures = []
210-
for target in targets:
211-
target_corpus_dir = os.path.join(corpus_dir, target)
228+
for target, t_env in targets:
229+
target_corpus_dir = corpus_dir / target
212230
os.makedirs(target_corpus_dir, exist_ok=True)
213231
use_value_profile = int(random.random() < .3)
214232
command = [
@@ -219,7 +237,7 @@ def job(command, t):
219237
f"-use_value_profile={use_value_profile}",
220238
target_corpus_dir,
221239
]
222-
futures.append(fuzz_pool.submit(job, command, target))
240+
futures.append(fuzz_pool.submit(job, command, target, t_env))
223241

224242
for future in as_completed(futures):
225243
future.result()

0 commit comments

Comments
 (0)