Skip to content

Commit 57fff48

Browse files
committed
Fix multiple security and code quality issues
This commit addresses 13 issues identified in code review: Critical Issues Fixed: - Fix NULL pointer dereference in https_resp_cb by checking NULL before use - Fix NULL pointer dereference in hostname_from_url by validating curl_url_get result - Fix buffer overflow in addr_list_reduced by validating IP address length - Fix incorrect fallthrough in https_set_request_version switch statement - Fix potential integer underflow in dns_poll_cb snprintf calculation - Add validation for DNS request sizes from network to prevent DoS attacks - Fix potential memory leak in https_fetch_ctx_init error path - Fix NULL pointer dereference risk in ring_buffer_free Medium Priority Issues Fixed: - Fix signed/unsigned loop logic in dns_server_tcp_respond send loop - Fix type mismatch in parse_int by using INT_MAX instead of INT32_MAX - Add portability fallback for accept4 on non-Linux systems - Fix typo: "listaning" -> "listening" in error message - Improve documentation for get_io_event return value handling These fixes improve security, portability, and code robustness.
1 parent 7b27ecd commit 57fff48

File tree

5 files changed

+84
-21
lines changed

5 files changed

+84
-21
lines changed

src/dns_server_tcp.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,36 @@
88
#include "dns_server_tcp.h"
99
#include "logging.h"
1010

11+
// Portability wrapper for accept4
12+
#ifndef __linux__
13+
// On non-Linux systems, implement accept4 using accept + fcntl
14+
static int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) {
15+
int fd = accept(sockfd, addr, addrlen);
16+
if (fd == -1) {
17+
return -1;
18+
}
19+
if (flags & SOCK_NONBLOCK) {
20+
int fl = fcntl(fd, F_GETFL, 0);
21+
if (fl == -1 || fcntl(fd, F_SETFL, fl | O_NONBLOCK) == -1) {
22+
int saved_errno = errno;
23+
close(fd);
24+
errno = saved_errno;
25+
return -1;
26+
}
27+
}
28+
if (flags & SOCK_CLOEXEC) {
29+
int fl = fcntl(fd, F_GETFD, 0);
30+
if (fl == -1 || fcntl(fd, F_SETFD, fl | FD_CLOEXEC) == -1) {
31+
int saved_errno = errno;
32+
close(fd);
33+
errno = saved_errno;
34+
return -1;
35+
}
36+
}
37+
return fd;
38+
}
39+
#endif
40+
1141
// the following macros require to have client pointer to tcp_client_s structure
1242
// else: compilation failure will occur
1343
#define LOG_CLIENT(level, format, args...) LOG(level, "C-%u: " format, client->id, ## args)
@@ -95,6 +125,11 @@ static int get_dns_request(struct tcp_client_s *client,
95125
char ** dns_req, uint16_t * req_size) {
96126
// check if whole request is available
97127
*req_size = ntohs(*((uint16_t*)client->input_buffer));
128+
// validate size before using it
129+
if (*req_size > DNS_REQUEST_BUFFER_SIZE || *req_size < DNS_HEADER_LENGTH) {
130+
WLOG_CLIENT("Invalid DNS request size from network: %u", *req_size);
131+
return -1; // Invalid size
132+
}
98133
uint16_t data_size = sizeof(uint16_t) + *req_size;
99134
if (data_size > client->input_buffer_used) {
100135
return 0; // Partial request
@@ -157,7 +192,8 @@ static void read_cb(struct ev_loop __attribute__((unused)) *loop,
157192
char *dns_req = NULL;
158193
uint16_t req_size = 0;
159194
uint8_t request_received = 0;
160-
while (get_dns_request(client, &dns_req, &req_size)) {
195+
int result;
196+
while ((result = get_dns_request(client, &dns_req, &req_size)) > 0) {
161197
if (req_size < DNS_HEADER_LENGTH) {
162198
WLOG_CLIENT("Malformed request received, too short: %u", req_size);
163199
free(dns_req);
@@ -169,6 +205,13 @@ static void read_cb(struct ev_loop __attribute__((unused)) *loop,
169205
request_received = 1;
170206
}
171207

208+
// Handle error case (invalid size from network)
209+
if (result < 0) {
210+
WLOG_CLIENT("Invalid DNS request, disconnecting client");
211+
remove_client(client);
212+
return;
213+
}
214+
172215
if (request_received) {
173216
ev_timer_again(d->loop, &client->timer_watcher);
174217
}
@@ -257,7 +300,7 @@ static int get_tcp_listen_sock(struct addrinfo *listen_addrinfo) {
257300
}
258301

259302
if (listen(sock, LISTEN_BACKLOG) == -1) {
260-
FLOG("Error listaning on %s:%d TCP: %s (%d)", ipstr, port,
303+
FLOG("Error listening on %s:%d TCP: %s (%d)", ipstr, port,
261304
strerror(errno), errno);
262305
}
263306

@@ -344,8 +387,10 @@ void dns_server_tcp_respond(dns_server_tcp_t *d,
344387
remove_client(client);
345388
return;
346389
}
390+
// For EAGAIN/EWOULDBLOCK, don't add to sent, just retry
391+
} else {
392+
sent += len;
347393
}
348-
sent += len;
349394

350395
if (sent == (ssize_t)resp_len) {
351396
break;

src/https_client.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ static void https_set_request_version(https_client_t *client,
248248
switch (client->opt->use_http_version) {
249249
case 1:
250250
http_version_int = CURL_HTTP_VERSION_1_1;
251-
__attribute__((fallthrough));
251+
break;
252252
case 2:
253253
break;
254254
case 3:
@@ -285,8 +285,7 @@ static void https_fetch_ctx_init(https_client_t *client,
285285
ctx->cb_data = cb_data;
286286
ctx->buf = NULL;
287287
ctx->buflen = 0;
288-
ctx->next = client->fetches;
289-
client->fetches = ctx;
288+
ctx->next = NULL;
290289

291290
ASSERT_CURL_EASY_SETOPT(ctx, CURLOPT_RESOLVE, resolv);
292291

@@ -326,12 +325,18 @@ static void https_fetch_ctx_init(https_client_t *client,
326325
CURLMcode multi_code = curl_multi_add_handle(client->curlm, ctx->curl);
327326
if (multi_code != CURLM_OK) {
328327
ELOG_REQ("curl_multi_add_handle error %d: %s", multi_code, curl_multi_strerror(multi_code));
328+
// Clean up ctx without trying to remove from multi handle
329+
ctx->cb(ctx->cb_data, NULL, 0);
330+
curl_easy_cleanup(ctx->curl);
331+
free(ctx);
329332
if (multi_code == CURLM_ABORTED_BY_CALLBACK) {
330333
WLOG_REQ("Resetting HTTPS client to recover from faulty state!");
331334
https_client_reset(client);
332-
} else {
333-
https_fetch_ctx_cleanup(client, NULL, client->fetches, -1); // dropping current failed request
334335
}
336+
} else {
337+
// Only add to linked list if successfully added to multi handle
338+
ctx->next = client->fetches;
339+
client->fetches = ctx;
335340
}
336341
}
337342

@@ -590,6 +595,8 @@ static void timer_cb(struct ev_loop __attribute__((unused)) *loop,
590595
check_multi_info(c);
591596
}
592597

598+
// Get IO event for a given socket, or find an unused slot (fd == 0)
599+
// Returns NULL if no matching socket found or no free slot available
593600
static struct ev_io * get_io_event(struct ev_io io_events[], curl_socket_t sock) {
594601
for (int i = 0; i < HTTPS_SOCKET_LIMIT; i++) {
595602
if (io_events[i].fd == sock) {

src/main.c

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,15 @@ static int hostname_from_url(const char* url_in,
5858
if (rc == CURLUE_OK) {
5959
char *host = NULL;
6060
rc = curl_url_get(url, CURLUPART_HOST, &host, 0);
61-
const size_t host_len = strlen(host);
62-
if (rc == CURLUE_OK && host_len < hostname_len &&
63-
host[0] != '[' && host[host_len-1] != ']' && // skip IPv6 address
64-
!is_ipv4_address(host)) {
65-
strncpy(hostname, host, hostname_len-1);
66-
hostname[hostname_len-1] = '\0';
67-
res = 1; // success
61+
if (rc == CURLUE_OK && host != NULL) {
62+
const size_t host_len = strlen(host);
63+
if (host_len < hostname_len &&
64+
host[0] != '[' && host[host_len-1] != ']' && // skip IPv6 address
65+
!is_ipv4_address(host)) {
66+
strncpy(hostname, host, hostname_len-1);
67+
hostname[hostname_len-1] = '\0';
68+
res = 1; // success
69+
}
6870
}
6971
curl_free(host);
7072
}
@@ -88,10 +90,10 @@ static void sigpipe_cb(struct ev_loop __attribute__((__unused__)) *loop,
8890

8991
static void https_resp_cb(void *data, char *buf, size_t buflen) {
9092
request_t *req = (request_t *)data;
91-
DLOG("Received response for id: %hX, len: %zu", req->tx_id, buflen);
9293
if (req == NULL) {
93-
FLOG("%04hX: data NULL", req->tx_id);
94+
FLOG("https_resp_cb: data NULL");
9495
}
96+
DLOG("Received response for id: %hX, len: %zu", req->tx_id, buflen);
9597
if (buf != NULL) { // May be NULL for timeout, DNS failure, or something similar.
9698
if (buflen < DNS_HEADER_LENGTH) {
9799
WLOG("%04hX: Malformed response received, too short: %u", req->tx_id, buflen);
@@ -182,6 +184,10 @@ static int addr_list_reduced(const char* full_list, const char* list) {
182184
char current[50];
183185
const char *comma = strchr(pos, ',');
184186
size_t ip_len = (size_t)(comma ? comma - pos : end - pos);
187+
if (ip_len >= sizeof(current)) {
188+
WLOG("IP address too long: %zu bytes", ip_len);
189+
return 1;
190+
}
185191
strncpy(current, pos, ip_len);
186192
current[ip_len] = '\0';
187193

@@ -205,10 +211,10 @@ static void dns_poll_cb(const char* hostname, void *data,
205211
memset(buf, 0, sizeof(buf));
206212
if (strlen(hostname) > 254) { FLOG("Hostname too long."); }
207213
int ip_start = snprintf(buf, sizeof(buf) - 1, "%s:443:", hostname);
208-
if (ip_start < 0) {
209-
abort(); // must be impossible
214+
if (ip_start < 0 || ip_start >= (int)(sizeof(buf) - 1)) {
215+
FLOG("snprintf failed or buffer too small: %d", ip_start);
210216
}
211-
(void)snprintf(buf + ip_start, sizeof(buf) - 1 - (uint32_t)ip_start, "%s", addr_list);
217+
(void)snprintf(buf + ip_start, sizeof(buf) - (size_t)ip_start, "%s", addr_list);
212218
if (app->resolv == NULL) {
213219
systemd_notify_ready();
214220
}

src/options.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void options_init(struct Options *opt) {
5050
int parse_int(char * str) {
5151
char * endptr = NULL;
5252
unsigned long int value = strtoul(str, &endptr, 10);
53-
if (*endptr != '\0' || value > INT32_MAX) {
53+
if (*endptr != '\0' || value > INT_MAX) {
5454
return -1;
5555
}
5656
return (int)value;

src/ring_buffer.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ void ring_buffer_init(struct ring_buffer **rbp, uint32_t size)
3535
void ring_buffer_free(struct ring_buffer **rbp)
3636
{
3737
struct ring_buffer *rb = *rbp;
38+
if (!rb) {
39+
return;
40+
}
3841
if (!rb->storage) {
42+
free((void*) rb);
43+
*rbp = NULL;
3944
return;
4045
}
4146
for (uint32_t i = 0; i < rb->size; i++) {

0 commit comments

Comments
 (0)