/src/uWebSockets/fuzzing/TopicTree.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | #define WIN32_EXPORT |
2 | | |
3 | | #include "helpers.h" |
4 | | |
5 | | /* Test for the topic tree */ |
6 | | #include "../src/TopicTree.h" |
7 | | |
8 | | #include <memory> |
9 | | |
10 | | // std::vector<std::string_view> topics = {"", "one", "two", "three"}; |
11 | | |
12 | 3.49k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
13 | | /* Create topic tree */ |
14 | 2.86M | uWS::TopicTree<std::string, std::string_view> topicTree([](uWS::Subscriber *s, std::string &message, auto flags) { |
15 | | |
16 | | /* Depending on what publishing we do below (with or without empty strings), |
17 | | * this assumption can hold true or not. For now it should hold true */ |
18 | 2.86M | if (!message.length()) { |
19 | 0 | free((void *) -1); |
20 | 0 | } |
21 | | |
22 | | /* Break if we have no subscriptions (not really an error, just to bring more randomness) */ |
23 | 2.86M | if (s->topics.size() == 0) { |
24 | 541 | return true; |
25 | 541 | } |
26 | | |
27 | | /* Success */ |
28 | 2.86M | return false; |
29 | 2.86M | }); |
30 | | |
31 | | /* Holder for all manually allocated subscribers */ |
32 | 3.49k | std::map<uint32_t, uWS::Subscriber *> subscribers; |
33 | | |
34 | | /* Iterate the padded fuzz as chunks */ |
35 | 3.07M | makeChunked(makePadded(data, size), size, [&topicTree, &subscribers](const uint8_t *data, size_t size) { |
36 | | /* We need at least 5 bytes */ |
37 | 3.07M | if (size > 4) { |
38 | | /* Last of all is a string */ |
39 | 3.07M | std::string_view lastString((char *) data + 5, size - 5); |
40 | | |
41 | | /* Why not */ |
42 | 3.07M | topicTree.lookupTopic(lastString); |
43 | | |
44 | | /* First 4 bytes is the subscriber id */ |
45 | 3.07M | uint32_t id; |
46 | 3.07M | memcpy(&id, data, 4); |
47 | | |
48 | | /* Then one byte action */ |
49 | 3.07M | if (data[4] == 'S') { |
50 | | |
51 | | /* Some ridiculously long topics has to be cut short (OOM) */ |
52 | 1.05M | if (lastString.length() > 512) { |
53 | 24 | lastString = "too long!"; |
54 | 24 | } |
55 | | |
56 | | /* Subscribe */ |
57 | 1.05M | if (subscribers.find(id) == subscribers.end()) { |
58 | | |
59 | | /* Limit number of subscribers to 100 (OOM) */ |
60 | 876k | if (subscribers.size() > 100) { |
61 | 842 | return; |
62 | 842 | } |
63 | | |
64 | 875k | uWS::Subscriber *subscriber = topicTree.createSubscriber(); |
65 | 875k | subscribers[id] = subscriber; |
66 | 875k | topicTree.subscribe(subscriber, lastString); |
67 | 875k | } else { |
68 | | /* Limit per subscriber subscriptions (OOM) */ |
69 | 179k | uWS::Subscriber *subscriber = subscribers[id]; |
70 | 179k | if (subscriber->topics.size() < 50) { |
71 | 176k | topicTree.subscribe(subscriber, lastString); |
72 | 176k | } |
73 | 179k | } |
74 | 2.02M | } else if (data[4] == 'U') { |
75 | | /* Unsubscribe */ |
76 | 25.3k | auto it = subscribers.find(id); |
77 | 25.3k | if (it != subscribers.end()) { |
78 | 22.3k | topicTree.unsubscribe(it->second, lastString); |
79 | 22.3k | } |
80 | 1.99M | } else if (data[4] == 'F') { |
81 | | /* Free subscriber */ |
82 | 906k | auto it = subscribers.find(id); |
83 | 906k | if (it != subscribers.end()) { |
84 | 851k | topicTree.freeSubscriber(it->second); |
85 | 851k | subscribers.erase(it); |
86 | 851k | } |
87 | 1.08M | } else if (data[4] == 'A') { |
88 | | /* Unsubscribe from all */ |
89 | 8.09k | auto it = subscribers.find(id); |
90 | 8.09k | if (it != subscribers.end()) { |
91 | 6.51k | std::vector<std::string> topics; |
92 | 63.7k | for (auto *topic : it->second->topics) { |
93 | 63.7k | topics.push_back(topic->name); |
94 | 63.7k | } |
95 | | |
96 | 63.7k | for (std::string &topic : topics) { |
97 | 63.7k | topicTree.unsubscribe(it->second, topic); |
98 | 63.7k | } |
99 | 6.51k | } |
100 | 1.08M | } else if (data[4] == 'O') { |
101 | | /* Drain one socket */ |
102 | 2.58k | auto it = subscribers.find(id); |
103 | 2.58k | if (it != subscribers.end()) { |
104 | 1.09k | topicTree.drain(it->second); |
105 | 1.09k | } |
106 | 1.07M | } else if (data[4] == 'P') { |
107 | | /* Publish only if we actually have data */ |
108 | 818k | if (lastString.length()) { |
109 | 6.73k | topicTree.publish(nullptr, lastString, std::string(lastString)); |
110 | 811k | } else { |
111 | | /* We could use having more strings */ |
112 | 811k | topicTree.publish(nullptr, "", "anything"); |
113 | 811k | } |
114 | 818k | } else { |
115 | | /* Drain for everything else (OOM) */ |
116 | 260k | topicTree.drain(); |
117 | 260k | } |
118 | 3.07M | } |
119 | 3.07M | }); |
120 | | |
121 | | /* Remove any subscriber from the tree */ |
122 | 23.7k | for (auto &p : subscribers) { |
123 | 23.7k | topicTree.freeSubscriber(p.second); |
124 | 23.7k | } |
125 | | |
126 | 3.49k | return 0; |
127 | 3.49k | } |
128 | | |