From 1887053ecf8b3cbe5758282ee1e9db083db632a4 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Tue, 3 Nov 2026 08:20:51 +0000 Subject: [PATCH 1/2] OSS-Fuzz: Add new fuzzer/corpus/dict targets tree exec Signed-off-by: Arthur Chan --- cmake/fuzzing_build.cmake | 2 +- fuzzing/bt_tree_exec_fuzzer.cpp | 434 ++++++++++++++++++ .../ifthenelse-whiledoelse | Bin 0 -> 42 bytes fuzzing/corpus/bt_tree_exec_fuzzer/loopint | Bin 0 -> 27 bytes .../corpus/bt_tree_exec_fuzzer/parallel-sleep | Bin 0 -> 32 bytes .../bt_tree_exec_fuzzer/parallelall-delay | Bin 0 -> 33 bytes .../bt_tree_exec_fuzzer/precondition-loopint | Bin 0 -> 31 bytes .../reactiveseq-repeat-retry | Bin 0 -> 35 bytes .../seq-script-timeout-repeat | Bin 0 -> 35 bytes .../bt_tree_exec_fuzzer/seqmem-set-unset | Bin 0 -> 32 bytes .../bt_tree_exec_fuzzer/substitution-named | Bin 0 -> 28 bytes .../corpus/bt_tree_exec_fuzzer/subtree-doc | Bin 0 -> 28 bytes fuzzing/corpus/bt_tree_exec_fuzzer/switch2 | Bin 0 -> 32 bytes fuzzing/corpus/bt_tree_exec_fuzzer/trycatch | Bin 0 -> 31 bytes fuzzing/dictionaries/bt_tree_exec_fuzzer.dict | 66 +++ 15 files changed, 501 insertions(+), 1 deletion(-) create mode 100644 fuzzing/bt_tree_exec_fuzzer.cpp create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/ifthenelse-whiledoelse create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/loopint create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/parallel-sleep create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/parallelall-delay create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/precondition-loopint create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/reactiveseq-repeat-retry create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/seq-script-timeout-repeat create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/seqmem-set-unset create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/substitution-named create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/subtree-doc create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/switch2 create mode 100644 fuzzing/corpus/bt_tree_exec_fuzzer/trycatch create mode 100644 fuzzing/dictionaries/bt_tree_exec_fuzzer.dict diff --git a/cmake/fuzzing_build.cmake b/cmake/fuzzing_build.cmake index 43f367001..354287121 100644 --- a/cmake/fuzzing_build.cmake +++ b/cmake/fuzzing_build.cmake @@ -106,7 +106,7 @@ endfunction() # Function to add fuzzing targets - compatible with both local and OSS-Fuzz builds function(add_fuzzing_targets) - set(FUZZERS bt_fuzzer script_fuzzer bb_fuzzer) + set(FUZZERS bt_fuzzer script_fuzzer bb_fuzzer bt_tree_exec_fuzzer) foreach(fuzzer ${FUZZERS}) add_executable(${fuzzer} fuzzing/${fuzzer}.cpp) diff --git a/fuzzing/bt_tree_exec_fuzzer.cpp b/fuzzing/bt_tree_exec_fuzzer.cpp new file mode 100644 index 000000000..63cec6e54 --- /dev/null +++ b/fuzzing/bt_tree_exec_fuzzer.cpp @@ -0,0 +1,434 @@ +// Fuzz target: bt_tree_exec_fuzzer +// +// Threat model (see fuzzing/README.md and the coverage-pipeline report): +// Behavior trees are frequently loaded from untrusted sources (files, network +// APIs, editor payloads). The existing bt_fuzzer covers XML *parsing* only: +// it never ticks a tree, so the whole execution engine (control nodes, +// decorators, tree_node/behavior_tree state machines, ports at runtime, +// substitution rules, wake-up/timer machinery) is dark in production coverage. +// +// This harness generates *structurally valid* XML over the builtin node set, +// creates the tree, and actually drives it: repeated tickRoot() calls until a +// terminal status, halt() while RUNNING, re-tick after halt, SubTree +// references, scripting enums, substitution rules (incl. wildcard filters) and +// a typed blackboard. All generated values are bounded (depth, children, +// Repeat/Retry counts never -1, Sleep/Timeout/Delay/Delayed-Test in the low +// milliseconds) so that no input can make the harness hang or blow memory. +// +// Expected bug classes: state-machine logic violations, use-after-free/dangling +// node handling on halt-while-RUNNING, out-of-bounds child indexing in control +// nodes, numeric parsing of attributes, scripting runtime errors. + +#include "behaviortree_cpp/bt_factory.h" +#include "behaviortree_cpp/blackboard.h" +#include "behaviortree_cpp/xml_parsing.h" + +#include + +#include +#include +#include +#include + +namespace +{ + +constexpr int kMaxDepth = 3; +constexpr unsigned kMaxTicks = 8; + +const char* kNames[] = { "n1", "act", "cond", "root_task", "aux" }; + +// Scripts used by Script/ScriptCondition/Precondition/TestNode. All short, +// terminating and side-effect limited to the blackboard. +const char* kConditionScripts[] = { + "var > 0", "var < 5", "flag", "!flag", "dbl < 10.0", "str == 'abc'", + "var == MODE_A" +}; +const char* kActionScripts[] = { + "var = var + 1", "var = 3", "var = var - 1", "str = 'fuzz'", + "dbl = var * 0.5", "flag = !flag", "var = MODE_A", "flag = var > 2" +}; +const char* kLiterals[] = { "1", "2", "true", "false", "1.5", "hello", "{var}" }; +const char* kStatuses[] = { "SUCCESS", "FAILURE", "SKIPPED" }; + +std::string pickName(FuzzedDataProvider& fdp) +{ + return fdp.PickValueInArray(kNames); +} + +std::string pickScript(FuzzedDataProvider& fdp) +{ + return fdp.PickValueInArray(kActionScripts); +} + +std::string pickCondition(FuzzedDataProvider& fdp) +{ + return fdp.PickValueInArray(kConditionScripts); +} + +// A condition-like leaf: ScriptCondition / AlwaysSuccess / AlwaysFailure. +std::string genConditionLeaf(FuzzedDataProvider& fdp) +{ + switch(fdp.ConsumeIntegralInRange(0, 2)) + { + case 0: + return ""; + case 1: + return ""; + default: + return ""; + } +} + +// Leaf actions. Occasionally blackboard-remap a port ("{key}" syntax). +std::string genLeaf(FuzzedDataProvider& fdp) +{ + switch(fdp.ConsumeIntegralInRange(0, 8)) + { + case 0: + return ""; + case 1: + return ""; + case 2: + return "