Skip to content

Commit 5af1707

Browse files
committed
Fix dangling hostname pointer in dns_poller; review cleanups
The hostname buffer passed to dns_poller_init() is block-scoped in main() since 6b4de73, but the poller stored the raw pointer and read it on every poll cycle after the block exited — use of a dead stack object. Copy the hostname in dns_poller_init() and free it in dns_poller_cleanup() instead of relying on the caller's buffer lifetime. The poller owns the copy, so the struct member is a plain char * (no const cast needed to free it). Also: - flipped clang-tidy from wildcard check to finer grained checks to avoid pulling in irrelevant stuff like altera-struct-pack-align. - main.c: check the dns_poller calloc for NULL like every other allocation site; validate -R as an IPv4/IPv6 literal before building the curl resolve entry; warn when -R is ignored because the resolver URL already contains an IP address - doh_proxy.c: don't crash if doh_proxy_await_bootstrap() was given a NULL callback
1 parent 0305226 commit 5af1707

10 files changed

Lines changed: 52 additions & 26 deletions

CMakeLists.txt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,20 @@ if(USE_CLANG_TIDY)
131131
message(STATUS "clang-tidy not found.")
132132
else()
133133
message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}")
134-
set(CLANG_TIDY_CHECKS "-checks=*,-readability-identifier-length,-altera-unroll-loops,-concurrency-mt-unsafe,")
135-
string(APPEND CLANG_TIDY_CHECKS "-bugprone-easily-swappable-parameters,-*magic-numbers,-hicpp-signed-bitwise,")
136-
string(APPEND CLANG_TIDY_CHECKS "-readability-function-cognitive-complexity,-altera-id-dependent-backward-branch,")
137-
string(APPEND CLANG_TIDY_CHECKS "-misc-include-cleaner,-llvmlibc-restrict-system-libc-headers,")
134+
# Opt in to the check modules relevant to portable C instead of "*": the
135+
# wildcard also enables domain-specific modules (altera-* for FPGA/OpenCL
136+
# kernels, llvmlibc-*, android-*, ...) whose fixits have previously written
137+
# inappropriate attributes into this codebase via -fix (see altera-struct-
138+
# pack-align: extended alignment is UB on calloc'd structs, and packing
139+
# misaligns embedded sockaddr_storage/libev watchers).
140+
set(CLANG_TIDY_CHECKS "-checks=-*,bugprone-*,cert-*,clang-analyzer-*,concurrency-*,")
141+
string(APPEND CLANG_TIDY_CHECKS "misc-*,performance-*,portability-*,readability-*,")
142+
string(APPEND CLANG_TIDY_CHECKS "-readability-identifier-length,-concurrency-mt-unsafe,")
143+
string(APPEND CLANG_TIDY_CHECKS "-bugprone-easily-swappable-parameters,-*magic-numbers,")
144+
string(APPEND CLANG_TIDY_CHECKS "-readability-function-cognitive-complexity,-misc-include-cleaner,")
138145
string(APPEND CLANG_TIDY_CHECKS "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,")
139146
string(APPEND CLANG_TIDY_CHECKS "-*function-size,-clang-diagnostic-*variadic-macro-arguments*")
140-
set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-fix" "-fix-errors" "${CLANG_TIDY_CHECKS}")
147+
set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "${CLANG_TIDY_CHECKS}")
141148
endif()
142149
else()
143150
message(STATUS "Not using clang-tidy.")

src/dns_listener_tcp.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ struct tcp_client_s {
5555
ev_timer timer_watcher;
5656

5757
struct tcp_client_s * next;
58-
} __attribute__((packed)) __attribute__((aligned(128)));
58+
};
5959

6060
struct dns_listener_tcp_s {
6161
dns_listener_t base;
@@ -73,7 +73,7 @@ struct dns_listener_tcp_s {
7373
uint16_t client_count;
7474
uint16_t client_limit;
7575
struct tcp_client_s * clients;
76-
} __attribute__((packed)) __attribute__((aligned(128)));
76+
};
7777

7878

7979
static void remove_client(struct tcp_client_s * client) {
@@ -332,7 +332,7 @@ static void tcp_respond(dns_listener_t *self, struct sockaddr *raddr,
332332
// Limit response size to prevent overflow when accounting for the 2-byte
333333
// length prefix. The total on-wire size would be resp_len + sizeof(uint16_t).
334334
if (resp_len < DNS_HEADER_LENGTH || resp_len > TCP_DNS_MAX_PAYLOAD) {
335-
WLOG("Malformed response received, invalid length: %u", resp_len);
335+
WLOG("Malformed response received, invalid length: %zu", resp_len);
336336
return;
337337
}
338338
const uint16_t response_id = ntohs(*((uint16_t*)resp));
@@ -355,7 +355,7 @@ static void tcp_respond(dns_listener_t *self, struct sockaddr *raddr,
355355
// below is a blocking syscall (not an event loop yield). If remove_client()
356356
// is called due to send errors, the function returns immediately.
357357

358-
DLOG_CLIENT("Sending %u bytes", resp_len);
358+
DLOG_CLIENT("Sending %zu bytes", resp_len);
359359

360360
// send length of response
361361
uint16_t resp_size = htons((uint16_t)resp_len);

src/dns_listener_udp.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ typedef struct dns_listener_udp_s {
2121

2222
dns_request_fn cb;
2323
void *cb_data;
24-
} __attribute__((aligned(128))) dns_listener_udp_t;
24+
} dns_listener_udp_t;
2525

2626
// Creates and binds a listening UDP socket for incoming requests.
2727
static int get_listen_sock(struct addrinfo *listen_addrinfo) {
@@ -91,7 +91,7 @@ static void udp_respond(dns_listener_t *self, struct sockaddr *raddr,
9191
dns_listener_udp_t *d = (dns_listener_udp_t *)self;
9292

9393
if (dns_resp_len < DNS_HEADER_LENGTH) {
94-
WLOG("Malformed response received, invalid length: %u", dns_resp_len);
94+
WLOG("Malformed response received, invalid length: %zu", dns_resp_len);
9595
return;
9696
}
9797
dns_truncate_for_udp(dns_req, dns_req_len, dns_resp, &dns_resp_len);

src/dns_poller.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,10 @@ void dns_poller_init(dns_poller_t *d, struct ev_loop *loop,
237237
}
238238

239239
d->loop = loop;
240-
d->hostname = hostname;
240+
d->hostname = strdup(hostname);
241+
if (d->hostname == NULL) {
242+
FLOG("Out of mem");
243+
}
241244
d->family = family;
242245
set_bootstrap_source_addr(d->ares, source_addr, family);
243246
d->cb = cb;
@@ -269,5 +272,6 @@ void dns_poller_cleanup(dns_poller_t *d) {
269272
ares_destroy(d->ares);
270273
ev_timer_stop(d->loop, &d->timer);
271274
ares_library_cleanup();
275+
free(d->hostname);
272276
free(d->io_events);
273277
}

src/dns_poller.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ typedef void (*dns_poller_cb)(const char* hostname, void *data,
2121
typedef struct {
2222
ares_channel ares;
2323
struct ev_loop *loop;
24-
const char *hostname;
24+
char *hostname; // owned; strdup'd in init, freed in cleanup
2525
int family; // AF_UNSPEC for IPv4 or IPv6, AF_INET for IPv4 only.
2626
dns_poller_cb cb;
2727
int polling_interval;
@@ -40,8 +40,7 @@ typedef struct {
4040
// `source_addr` optionally binds bootstrap DNS lookups to a specific IP.
4141
// `family` should be AF_INET for IPv4 or AF_UNSPEC for both IPv4 and IPv6.
4242
//
43-
// Note: hostname *not* copied. It should remain valid until
44-
// dns_poller_cleanup called.
43+
// Note: hostname is copied; the caller's buffer need not outlive this call.
4544
void dns_poller_init(dns_poller_t *d, struct ev_loop *loop,
4645
const char *bootstrap_dns,
4746
int bootstrap_dns_polling_interval,

src/dns_truncate.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ static uint16_t get_edns_udp_size(const char *dns_req, const size_t dns_req_len)
7575
* 3. EDNS0/OPT Preservation (RFC 6891):
7676
* The OPT pseudo-RR (Type 41) is critical for extended error tracking, cookies, and DNSSEC signaling.
7777
* RFC 6891 mandates that OPT records should be preserved in truncated messages if they were present
78-
* in the request. This function scans the Additional section, locates the OPT record, and uses
79-
* memmove() to safely relocate it to sit directly flush against the end of the Question section,
80-
* preserving it in the truncated response stream.
78+
* in the request. This function scans the Additional section and removes every record except the
79+
* OPT one; re-serializing via c-ares then leaves the OPT record directly after the Question
80+
* section, preserving it in the truncated response stream.
8181
*
8282
* 4. Trusted Data Assumption:
8383
* DoH resolver response is considered trusted input, so assuming that it complies with RFCs
@@ -142,7 +142,7 @@ static void truncate_to_size_limit(uint8_t *buf, size_t *buflen, size_t size_lim
142142
memcpy(buf, new_resp, new_resp_len);
143143
*buflen = new_resp_len;
144144
buf[2] |= 0x02; // set truncation flag
145-
ILOG("%04hX: DNS response size truncated from %u to %u to keep %u limit",
145+
ILOG("%04hX: DNS response size truncated from %zu to %zu to keep %zu limit",
146146
resp_id, old_size, new_resp_len, size_limit);
147147
}
148148

src/doh_proxy.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct doh_proxy {
2525
// fallback resolver and risk a recursion through our own listener).
2626
uint8_t awaiting_bootstrap;
2727
doh_proxy_bootstrap_done_cb bootstrap_done_cb;
28-
} __attribute__((aligned(64)));
28+
};
2929

3030
// Per-request transient state. Lives from doh_proxy_handle_request to
3131
// https_resp_cb, when the response (or failure) returns from libcurl.
@@ -36,7 +36,7 @@ typedef struct {
3636
struct sockaddr_storage raddr;
3737
char *dns_req;
3838
size_t dns_req_len;
39-
} __attribute__((packed)) __attribute__((aligned(128))) doh_request_t;
39+
} doh_request_t;
4040

4141
doh_proxy_t * doh_proxy_create(struct ev_loop *loop,
4242
https_client_t *client,
@@ -146,7 +146,9 @@ void doh_proxy_handle_resolver_update(const char *hostname, void *ctx,
146146

147147
if (p->awaiting_bootstrap) {
148148
p->awaiting_bootstrap = 0;
149-
p->bootstrap_done_cb();
149+
if (p->bootstrap_done_cb != NULL) {
150+
p->bootstrap_done_cb();
151+
}
150152
}
151153
}
152154

@@ -162,7 +164,7 @@ static void doh_response_cb(void *data, char *buf, size_t buflen) {
162164

163165
if (buf != NULL) { // NULL on timeout / DNS failure / similar.
164166
if (buflen < DNS_HEADER_LENGTH) {
165-
WLOG("%04hX: Malformed response received, too short: %u", req_id, buflen);
167+
WLOG("%04hX: Malformed response received, too short: %zu", req_id, buflen);
166168
} else {
167169
const uint16_t resp_id = ntohs(*((uint16_t*)buf));
168170
if (req_id != resp_id) {

src/main.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,16 @@
2424
#include "options.h"
2525
#include "stat.h"
2626

27-
static int is_ipv4_address(char *str) {
27+
static int is_ipv4_address(const char *str) {
2828
struct in6_addr addr;
2929
return inet_pton(AF_INET, str, &addr) == 1;
3030
}
3131

32+
static int is_ip_address(const char *str) {
33+
struct in6_addr addr;
34+
return is_ipv4_address(str) || inet_pton(AF_INET6, str, &addr) == 1;
35+
}
36+
3237
enum url_type {
3338
URL_TYPE_ERROR,
3439
URL_TYPE_IP,
@@ -305,13 +310,19 @@ int main(int argc, char *argv[]) {
305310
doh_proxy_set_port(proxy, port);
306311
if (opt.resolver_ip == NULL) {
307312
dns_poller = (dns_poller_t *)calloc(1, sizeof(dns_poller_t));
313+
if (dns_poller == NULL) {
314+
FLOG("Out of mem");
315+
}
308316
doh_proxy_await_bootstrap(proxy, systemd_notify_ready);
309317
dns_poller_init(dns_poller, loop, opt.bootstrap_dns,
310318
opt.bootstrap_dns_polling_interval, opt.source_addr,
311319
hostname, opt.ipv4 ? AF_INET : AF_UNSPEC,
312320
doh_proxy_handle_resolver_update, proxy);
313321
ILOG("DNS polling initialized for '%s'", hostname);
314322
} else {
323+
if (!is_ip_address(opt.resolver_ip)) {
324+
FLOG("Resolver IP override '%s' is not a valid IP literal", opt.resolver_ip);
325+
}
315326
const size_t resolv_buf_len = strlen(hostname) + 1 + PORT_STR_LENGTH + 1 + strlen(opt.resolver_ip) + 1;
316327
char * resolv_buf = (char *)calloc(resolv_buf_len, sizeof(char));
317328
(void)snprintf(resolv_buf, resolv_buf_len, "%s:%u:%s", hostname, port, opt.resolver_ip);
@@ -323,6 +334,9 @@ int main(int argc, char *argv[]) {
323334
break;
324335
case URL_TYPE_IP:
325336
doh_proxy_set_port(proxy, port);
337+
if (opt.resolver_ip != NULL) {
338+
WLOG("Resolver URL already contains an IP address, ignoring -R %s", opt.resolver_ip);
339+
}
326340
ILOG("Resolver prefix '%s' doesn't appear to contain a hostname. "
327341
"DNS polling disabled.", opt.resolver_url);
328342
systemd_notify_ready();

src/options.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ struct Options {
6666

6767
// Number of logs to be kept by flight recorder
6868
int flight_recorder_size;
69-
} __attribute__((aligned(128)));
69+
};
7070
typedef struct Options options_t;
7171

7272
enum OptionsParseResult {

src/ring_buffer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ struct ring_buffer
1515
uint32_t size;
1616
uint32_t next; // next slot to use in storage
1717
uint8_t full;
18-
} __attribute__((packed)) __attribute__((aligned(32)));
18+
};
1919

2020
void ring_buffer_init(struct ring_buffer **rbp, uint32_t size)
2121
{

0 commit comments

Comments
 (0)