/src/behaviortreecpp/fuzzing/bt_fuzzer.cpp
Line | Count | Source |
1 | | #include <fuzzer/FuzzedDataProvider.h> |
2 | | #include "behaviortree_cpp/bt_factory.h" |
3 | | #include "behaviortree_cpp/xml_parsing.h" |
4 | | #include <string> |
5 | | |
6 | | // List of valid node types we can use to construct valid-ish XML |
7 | | constexpr const char* NODE_TYPES[] = { |
8 | | "Sequence", "Fallback", "ParallelAll", |
9 | | "ReactiveSequence", "ReactiveFallback", "IfThenElse", |
10 | | "WhileDoElse", "Inverter", "RetryUntilSuccessful", |
11 | | "Repeat", "Timeout", "Delay", |
12 | | "ForceSuccess", "ForceFailure", "AlwaysSuccess", |
13 | | "AlwaysFailure", "SetBlackboard", "SubTree" |
14 | | }; |
15 | | |
16 | | // Attributes that can be added to nodes |
17 | | constexpr const char* NODE_ATTRIBUTES[] = { "name", "ID", "port_1", |
18 | | "port_2", "timeout_ms", "delay_ms", |
19 | | "threshold", "max_repeats" }; |
20 | | |
21 | | std::string generateFuzzedNodeXML(FuzzedDataProvider& fdp, int depth = 0) |
22 | 63.2k | { |
23 | | // Prevent stack overflow with max depth |
24 | 63.2k | if(depth > 6) |
25 | 0 | { // Reasonable limit for XML tree depth |
26 | 0 | return "<AlwaysSuccess/>"; |
27 | 0 | } |
28 | | |
29 | 63.2k | std::string xml; |
30 | 63.2k | const std::string node_type = fdp.PickValueInArray(NODE_TYPES); |
31 | | |
32 | 63.2k | xml += "<" + node_type; |
33 | | |
34 | 63.2k | size_t num_attributes = fdp.ConsumeIntegralInRange<size_t>(0, 3); |
35 | 152k | for(size_t i = 0; i < num_attributes; i++) |
36 | 89.5k | { |
37 | 89.5k | const std::string attr = fdp.PickValueInArray(NODE_ATTRIBUTES); |
38 | 89.5k | std::string value = fdp.ConsumeRandomLengthString(10); |
39 | 89.5k | xml += " " + attr + "=\"" + value + "\""; |
40 | 89.5k | } |
41 | | |
42 | 63.2k | if(depth > 3 || fdp.ConsumeBool()) |
43 | 26.6k | { |
44 | 26.6k | xml += "/>"; |
45 | 26.6k | } |
46 | 36.6k | else |
47 | 36.6k | { |
48 | 36.6k | xml += ">"; |
49 | | // Add some child nodes recursively with depth limit |
50 | 36.6k | size_t num_children = fdp.ConsumeIntegralInRange<size_t>(0, 2); |
51 | 72.5k | for(size_t i = 0; i < num_children; i++) |
52 | 35.9k | { |
53 | 35.9k | xml += generateFuzzedNodeXML(fdp, depth + 1); |
54 | 35.9k | } |
55 | 36.6k | xml += "</" + node_type + ">"; |
56 | 36.6k | } |
57 | | |
58 | 63.2k | return xml; |
59 | 63.2k | } |
60 | | |
61 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) |
62 | 16.2k | { |
63 | 16.2k | if(size < 4) |
64 | 2 | { |
65 | 2 | return 0; |
66 | 2 | } |
67 | | |
68 | 16.2k | FuzzedDataProvider fdp(data, size); |
69 | 16.2k | BT::BehaviorTreeFactory factory; |
70 | | |
71 | 16.2k | try |
72 | 16.2k | { |
73 | | // Strategy 1: Test with completely random data |
74 | 16.2k | if(fdp.ConsumeBool()) |
75 | 2.34k | { |
76 | 2.34k | std::string random_xml = fdp.ConsumeRandomLengthString(size - 1); |
77 | 2.34k | try |
78 | 2.34k | { |
79 | 2.34k | factory.createTreeFromText(random_xml); |
80 | 2.34k | } |
81 | 2.34k | catch(const std::exception&) |
82 | 2.34k | {} |
83 | 2.34k | } |
84 | | // Strategy 2: Generate semi-valid XML |
85 | 13.9k | else |
86 | 13.9k | { |
87 | 13.9k | std::string xml = R"( |
88 | 13.9k | <root BTCPP_format="4"> |
89 | 13.9k | <BehaviorTree ID="MainTree">)"; |
90 | | |
91 | 13.9k | size_t num_nodes = fdp.ConsumeIntegralInRange<size_t>(1, 5); |
92 | 41.3k | for(size_t i = 0; i < num_nodes; i++) |
93 | 27.3k | { |
94 | 27.3k | xml += generateFuzzedNodeXML(fdp); |
95 | 27.3k | } |
96 | | |
97 | 13.9k | xml += R"( |
98 | 13.9k | </BehaviorTree> |
99 | 13.9k | </root>)"; |
100 | | |
101 | 13.9k | auto blackboard = BT::Blackboard::create(); |
102 | | |
103 | 13.9k | switch(fdp.ConsumeIntegralInRange<int>(0, 2)) |
104 | 13.9k | { |
105 | 13.8k | case 0: |
106 | 13.8k | factory.createTreeFromText(xml, blackboard); |
107 | 13.8k | break; |
108 | 48 | case 1: |
109 | 48 | BT::VerifyXML(xml, {}); |
110 | 48 | break; |
111 | 53 | case 2: |
112 | 53 | factory.registerBehaviorTreeFromText(xml); |
113 | 53 | if(!factory.registeredBehaviorTrees().empty()) |
114 | 31 | { |
115 | 31 | factory.createTree(factory.registeredBehaviorTrees().front(), blackboard); |
116 | 31 | } |
117 | 53 | break; |
118 | 13.9k | } |
119 | 13.9k | } |
120 | 16.2k | } |
121 | 16.2k | catch(const std::exception&) |
122 | 16.2k | {} |
123 | | |
124 | 16.2k | return 0; |
125 | 16.2k | } |