Coverage Report

Created: 2025-10-14 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/uWebSockets/fuzzing/TopicTree.cpp
Line
Count
Source
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
2.99k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
13
    /* Create topic tree */
14
3.00M
    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
3.00M
        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
3.00M
        if (s->topics.size() == 0) {
24
1.39k
            return true;
25
1.39k
        }
26
27
        /* Success */
28
3.00M
        return false;
29
3.00M
    });
30
31
    /* Holder for all manually allocated subscribers */
32
2.99k
    std::map<uint32_t, uWS::Subscriber *> subscribers;
33
34
    /* Iterate the padded fuzz as chunks */
35
4.35M
    makeChunked(makePadded(data, size), size, [&topicTree, &subscribers](const uint8_t *data, size_t size) {
36
        /* We need at least 5 bytes */
37
4.35M
        if (size > 4) {
38
            /* Last of all is a string */
39
3.83M
            std::string_view lastString((char *) data + 5, size - 5);
40
            
41
            /* Why not */
42
3.83M
            topicTree.lookupTopic(lastString);
43
44
            /* First 4 bytes is the subscriber id */
45
3.83M
            uint32_t id;
46
3.83M
            memcpy(&id, data, 4);
47
48
            /* Then one byte action */
49
3.83M
            if (data[4] == 'S') {
50
51
                /* Some ridiculously long topics has to be cut short (OOM) */
52
1.19M
                if (lastString.length() > 512) {
53
18
                    lastString = "too long!";
54
18
                }
55
56
                /* Subscribe */
57
1.19M
                if (subscribers.find(id) == subscribers.end()) {
58
59
                    /* Limit number of subscribers to 100 (OOM) */
60
1.06M
                    if (subscribers.size() > 100) {
61
1.79k
                        return;
62
1.79k
                    }
63
64
1.06M
                    uWS::Subscriber *subscriber = topicTree.createSubscriber();
65
1.06M
                    subscribers[id] = subscriber;
66
1.06M
                    topicTree.subscribe(subscriber, lastString);
67
1.06M
                } else {
68
                    /* Limit per subscriber subscriptions (OOM) */
69
132k
                    uWS::Subscriber *subscriber = subscribers[id];
70
132k
                    if (subscriber->topics.size() < 50) {
71
130k
                        topicTree.subscribe(subscriber, lastString);
72
130k
                    }
73
132k
                }
74
2.63M
            } else if (data[4] == 'U') {
75
                /* Unsubscribe */
76
18.3k
                auto it = subscribers.find(id);
77
18.3k
                if (it != subscribers.end()) {
78
16.6k
                    topicTree.unsubscribe(it->second, lastString);
79
16.6k
                }
80
2.61M
            } else if (data[4] == 'F') {
81
                /* Free subscriber */
82
1.11M
                auto it = subscribers.find(id);
83
1.11M
                if (it != subscribers.end()) {
84
1.04M
                    topicTree.freeSubscriber(it->second);
85
1.04M
                    subscribers.erase(it);
86
1.04M
                }
87
1.50M
            } else if (data[4] == 'A') {
88
                /* Unsubscribe from all */
89
13.2k
                auto it = subscribers.find(id);
90
13.2k
                if (it != subscribers.end()) {
91
11.8k
                    std::vector<std::string> topics;
92
49.9k
                    for (auto *topic : it->second->topics) {
93
49.9k
                        topics.push_back(topic->name);
94
49.9k
                    }
95
96
49.9k
                    for (std::string &topic : topics) {
97
49.9k
                        topicTree.unsubscribe(it->second, topic);
98
49.9k
                    }
99
11.8k
                }
100
1.49M
            } else if (data[4] == 'O') {
101
                /* Drain one socket */
102
3.35k
                auto it = subscribers.find(id);
103
3.35k
                if (it != subscribers.end()) {
104
2.20k
                    topicTree.drain(it->second);
105
2.20k
                }
106
1.48M
            } else if (data[4] == 'P') {
107
                /* Publish only if we actually have data */
108
1.28M
                if (lastString.length()) {
109
13.9k
                    topicTree.publish(nullptr, lastString, std::string(lastString));
110
1.27M
                } else {
111
                    /* We could use having more strings */
112
1.27M
                    topicTree.publish(nullptr, "", "anything");
113
1.27M
                }
114
1.28M
            } else {
115
                /* Drain for everything else (OOM) */
116
202k
                topicTree.drain();
117
202k
            }
118
3.83M
        }
119
4.35M
    });
120
121
    /* Remove any subscriber from the tree */
122
20.3k
    for (auto &p : subscribers) {
123
20.3k
        topicTree.freeSubscriber(p.second);
124
20.3k
    }
125
126
2.99k
    return 0;
127
2.99k
}
128