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