-
Notifications
You must be signed in to change notification settings - Fork 486
/
Copy pathpcm-sensor-server-fuzz.cpp
245 lines (220 loc) · 7.85 KB
/
pcm-sensor-server-fuzz.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <netdb.h>
#include <netinet/tcp.h>
#define UNIT_TEST 1
#include "../src/pcm-sensor-server.cpp"
#undef UNIT_TEST
int port = 0;
bool waitForPort(int port, int timeoutSeconds) {
int sockfd;
struct sockaddr_in address;
bool isBound = false;
time_t startTime = time(nullptr);
// Create a socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
DBG( 0, "Client: Error creating socket" );
return false;
}
// Set up the address structure
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(port);
// Loop until the port is bound or the timeout is reached
while (!isBound && (time(nullptr) - startTime) < timeoutSeconds) {
// Attempt to connect to the port
if (connect(sockfd, (struct sockaddr *)&address, sizeof(address)) < 0) {
// Connection failed, wait a bit before retrying
sleep(1);
} else {
// Connection succeeded, the port is bound
isBound = true;
}
}
// Clean up the socket
close(sockfd);
return isBound;
}
HTTPServer * httpServer;
std::thread * serverThread;
void cleanup()
{
DBG( 0, "Client: Stopping HTTPServer" );
httpServer->stop();
DBG( 0, "Client: Cleaning up PMU:" );
PCM::getInstance()->cleanup();
}
bool init()
{
port = (rand() % 100) + 10000; // to be able to restart the fuzzer quickly without waiting for the port to be released
serverThread = new std::thread([]() {
PCM::ErrorCode status;
PCM * pcmInstance = PCM::getInstance();
assert(pcmInstance);
pcmInstance->resetPMU();
status = pcmInstance->program();
if (status != PCM::Success) {
DBG( 0, "Client: Error in program() function" );
exit(1);
}
debug::dyn_debug_level(1);
#ifdef FUZZ_USE_SSL
DBG( 0, "Client: Starting SSL enabled server on https://localhost:", port );
auto httpsServer = new HTTPSServer( "", port );
httpsServer->setPrivateKeyFile ( "/private.key" );
httpsServer->setCertificateFile( "/certificate.crt" );
httpsServer->initialiseSSL();
httpServer = httpsServer;
#else
DBG( 0, "Client: Starting plain HTTP server on http://localhost:", port );
httpServer = new HTTPServer( "", port );
#endif
// HEAD is GET without body, we will remove the body in execute()
httpServer->registerCallback( HTTPRequestMethod::GET, my_get_callback );
httpServer->registerCallback( HTTPRequestMethod::HEAD, my_get_callback );
httpServer->run();
});
int timeout = 60; // Timeout in seconds
DBG( 0, "Client: Waiting for port ", port, " to be bound with timeout of ", timeout, " seconds..." );
if (waitForPort(port, timeout)) {
DBG( 0, "Client: Port ", port, " is now bound." );
} else {
DBG( 0, "Client: Port ", port, " is not bound after ", timeout, " seconds." );
exit(1);
}
atexit(cleanup);
return true;
}
std::string make_request(const std::string& request) {
#ifdef FUZZ_USE_SSL
const SSL_METHOD* method = TLS_client_method();
SSL_CTX* ctx = SSL_CTX_new(method);
if (!ctx) {
throw std::runtime_error("Unable to create SSL context");
}
#endif
std::string server = "127.0.0.1";
// Resolve the host
struct hostent* host = gethostbyname(server.c_str());
if (!host) {
#ifdef FUZZ_USE_SSL
SSL_CTX_free(ctx);
#endif
DBG( 0, "Client: Failed to resolve host. Error: ", strerror(errno) );
throw std::runtime_error("Failed to resolve host: " + server);
}
// Create socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
#ifdef FUZZ_USE_SSL
SSL_CTX_free(ctx);
#endif
DBG( 0, "Client: Failed to create socket. Error: ", strerror(errno) );
throw std::runtime_error("Failed to create socket");
}
// Create server address structure
struct sockaddr_in server_addr;
std::memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
std::memcpy(&server_addr.sin_addr, host->h_addr, host->h_length);
// Connect to server
if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
DBG( 0, "Failed to connect to server. Error: ", strerror(errno) );
close(sock);
#ifdef FUZZ_USE_SSL
SSL_CTX_free(ctx);
#endif
throw std::runtime_error("Failed to connect to server");
}
#ifdef FUZZ_USE_SSL
// Create SSL structure
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
int con_ret = SSL_connect(ssl);
DBG( 1, "Client: SSL_connect returned ", con_ret );
if ( con_ret <= 0) {
SSL_free(ssl);
close(sock);
SSL_CTX_free(ctx);
throw std::runtime_error("Failed to establish SSL connection");
}
#endif
#ifdef FUZZ_USE_SSL
// "Client:" is used as a hint to indicate whether client or server wrote the debug messages inside socketstream and socketbuf
// When using this socketstream, it takes ownership of the socket and ssl connection and is responsible for properly closing
// connections and freeing the allocated structures, this is why all of the frees and closes are commented out below
DBG( 0, "Client: Opening an SSL socket stream" );
socketstream mystream( sock, ssl, "Client: " );
#else
DBG( 0, "Client: Opening a normal socket stream" );
socketstream mystream( sock );
#endif
DBG( 0, "Sending request: \n", request, "\n=====" );
std::string response;
int bytes_received = -1;
// Send the request
try {
mystream << request.c_str();
mystream.sync();
} catch ( const std::exception& e ) {
DBG( 0, "Writing caused an exception: ", e.what() );
mystream.close();
#ifdef FUZZ_USE_SSL
SSL_CTX_free(ctx);
#endif
throw std::runtime_error(std::string("Client Failed to write the request: ") + e.what());
}
// Receive the response
HTTPResponse resp;
DBG( 0, "Client: Waiting for response:" );
try {
mystream >> resp;
} catch ( const std::exception& e ) {
mystream.close();
#ifdef FUZZ_USE_SSL
SSL_CTX_free(ctx);
#endif
DBG( 0, "Reading from the socket failed, reason: ", e.what() );
return std::string("Not necessarily fatal: Client: Exception caught while reading a response from the server: ") + e.what();
}
// We've got a valid HTTPResponse otherwise we'd have caught an exception above
HTTPHeader const h = resp.getHeader( "Content-Length" );
size_t contentLength = h.headerValueAsNumber();
// contentLength must be positive now otherwise the bad Content-Length should have thrown an exception
bytes_received = contentLength;
DBG( 0, "Client: received ", bytes_received, " bytes, copying them into response." );
// Reducing verbosity, only print the first 1024 characters of the response
response.append( resp.body() );
if ( response.size() > 1024 )
response.erase(1024, std::string::npos );
// clean up
mystream.close();
#ifdef FUZZ_USE_SSL
SSL_CTX_free(ctx);
#endif
return response;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
static bool initialized = false;
if (!initialized) {
initialized = init();
}
try {
std::string request = std::string((const char*)data, size);
std::string response = make_request(request);
DBG( 0, "Response:\n", response, "\n====" );
} catch (const std::exception& e) {
DBG( 0, "Client: LLVMFuzzerTestOneInput Exception: \"", e.what(), "\"" );
exit(1);
}
return 0;
}