From 55ae77915a8bf3d9e50ed44b554f0d368f5de9c6 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:37:47 -0700 Subject: [PATCH 01/11] Null-terminate HTTP recv buffer before request parsing --- src/http/httpd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/http/httpd.c b/src/http/httpd.c index 47c1b4b2..6effb456 100644 --- a/src/http/httpd.c +++ b/src/http/httpd.c @@ -466,7 +466,7 @@ static void http_recv_cb(int sd, uint16_t event, void *arg) { if (!hc) return; (void) event; if (hc->ssl) { - ret = wolfSSL_read(hc->ssl, buf, sizeof(buf)); + ret = wolfSSL_read(hc->ssl, buf, sizeof(buf) - 1); if (ret < 0) { if (wolfSSL_get_error(hc->ssl, ret) == WOLFSSL_ERROR_WANT_READ) { return; @@ -475,12 +475,14 @@ static void http_recv_cb(int sd, uint16_t event, void *arg) { } } } else { - ret = wolfIP_sock_recv(hc->httpd->ipstack, sd, buf, sizeof(buf), 0); + ret = wolfIP_sock_recv(hc->httpd->ipstack, sd, buf, sizeof(buf) - 1, 0); if (ret == -WOLFIP_EAGAIN) return; } if (ret <= 0) goto fail_close; + /* The parser uses strchr/strstr, so terminate the bounded read. */ + buf[ret] = '\0'; parse_r = parse_http_request(hc, buf, ret); if (parse_r < 0) goto fail_close; From 7ed7064c4e05d26dca71ceb89d40afdbfd5edb67 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:38:05 -0700 Subject: [PATCH 02/11] Close accepted HTTP socket when client table is full --- src/http/httpd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/http/httpd.c b/src/http/httpd.c index 6effb456..5479eb25 100644 --- a/src/http/httpd.c +++ b/src/http/httpd.c @@ -505,11 +505,13 @@ static void http_accept_cb(int sd, uint16_t event, void *arg) { struct wolfIP_sockaddr_in addr; socklen_t addr_len = sizeof(struct wolfIP_sockaddr_in); int client_sd = wolfIP_sock_accept(httpd->ipstack, sd, (struct wolfIP_sockaddr *) &addr, &addr_len); + int i; + int assigned = 0; if (client_sd < 0) { return; } (void) event; - for (int i = 0; i < HTTPD_MAX_CLIENTS; i++) { + for (i = 0; i < HTTPD_MAX_CLIENTS; i++) { if (httpd->clients[i].client_sd == 0) { httpd->clients[i].client_sd = client_sd; httpd->clients[i].httpd = httpd; @@ -526,9 +528,12 @@ static void http_accept_cb(int sd, uint16_t event, void *arg) { } } wolfIP_register_callback(httpd->ipstack, client_sd, http_recv_cb, &httpd->clients[i]); + assigned = 1; break; } } + if (!assigned) + wolfIP_sock_close(httpd->ipstack, client_sd); } /* Extra utility to extract requests arguments */ From d38a00b1897e3f71fe57c941a444e926aabf4202 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:38:17 -0700 Subject: [PATCH 03/11] Write URL-encode terminator at end of buffer --- src/http/httpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/httpd.c b/src/http/httpd.c index 5479eb25..1611c99c 100644 --- a/src/http/httpd.c +++ b/src/http/httpd.c @@ -290,7 +290,7 @@ int http_url_encode(char *buf, size_t len, size_t max_len) { if (q) { if (len >= max_len) return -1; /* No space for the null terminator */ - q[len] = '\0'; + buf[len] = '\0'; } return len; } From 59844d9669aea747b3bcb0469caaabacdf7b4c5d Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:38:33 -0700 Subject: [PATCH 04/11] Size chunk-length buffer for full hex range --- src/http/httpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/httpd.c b/src/http/httpd.c index 1611c99c..81e2a21f 100644 --- a/src/http/httpd.c +++ b/src/http/httpd.c @@ -181,7 +181,7 @@ static void http_close_client(struct http_client *hc) } void http_send_response_chunk(struct http_client *hc, const void *chunk, size_t len) { - char txt_chunk[8]; + char txt_chunk[20]; memset(txt_chunk, 0, sizeof(txt_chunk)); if (!hc) return; From 7c8753e8d7c7b5b757e60953c61ed81f0e1450d4 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:39:03 -0700 Subject: [PATCH 05/11] Track HTTP route slot occupancy by path not handler --- src/http/httpd.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/http/httpd.c b/src/http/httpd.c index 81e2a21f..6fa61bd8 100644 --- a/src/http/httpd.c +++ b/src/http/httpd.c @@ -71,8 +71,9 @@ static const char *http_status_text(int status_code) { } int httpd_register_handler(struct httpd *httpd, const char *path, int (*handler)(struct httpd *httpd, struct http_client *hc, struct http_request *req)) { - for (int i = 0; i < HTTPD_MAX_URLS; i++) { - if (httpd->urls[i].handler == NULL) { + int i; + for (i = 0; i < HTTPD_MAX_URLS; i++) { + if (httpd->urls[i].path[0] == '\0') { /* Copy path and guarantee null termination */ strncpy(httpd->urls[i].path, path, HTTP_PATH_LEN); httpd->urls[i].path[HTTP_PATH_LEN-1] = '\0'; @@ -84,8 +85,9 @@ int httpd_register_handler(struct httpd *httpd, const char *path, int (*handler) } int httpd_register_static_page(struct httpd *httpd, const char *path, const char *content) { - for (int i = 0; i < HTTPD_MAX_URLS; i++) { - if (httpd->urls[i].handler == NULL) { + int i; + for (i = 0; i < HTTPD_MAX_URLS; i++) { + if (httpd->urls[i].path[0] == '\0') { /* Copy path and guarantee null termination */ strncpy(httpd->urls[i].path, path, HTTP_PATH_LEN); httpd->urls[i].path[HTTP_PATH_LEN-1] = '\0'; From 632e2c7c51a1479c0d77a8c22109e144dd721ad2 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:40:39 -0700 Subject: [PATCH 06/11] Parse HTTP query string from the request target --- src/http/httpd.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/http/httpd.c b/src/http/httpd.c index 6fa61bd8..5f859c95 100644 --- a/src/http/httpd.c +++ b/src/http/httpd.c @@ -321,7 +321,10 @@ static int parse_http_request(struct http_client *hc, uint8_t *buf, size_t len) char *p = (char *) buf; char *end = p + len; char *q; + char *query_start = NULL; size_t n; + size_t path_len; + size_t query_len; int ret; int decoded_len; long content_length = -1; /* -1: no Content-Length header seen */ @@ -345,26 +348,40 @@ static int parse_http_request(struct http_client *hc, uint8_t *buf, size_t len) if (!q) goto bad_request; n = q - p; - if (n >= sizeof(req.path)) + /* Split the request target into path and optional query (on '?'). */ + query_start = memchr(p, '?', n); + if (query_start != NULL) { + path_len = (size_t)(query_start - p); + query_len = n - path_len - 1U; + } else { + path_len = n; + query_len = 0; + } + if (path_len >= sizeof(req.path)) goto bad_request; - memcpy(req.path, p, n); - req.path[n] = '\0'; - decoded_len = http_url_decode(req.path, n); + memcpy(req.path, p, path_len); + req.path[path_len] = '\0'; + decoded_len = http_url_decode(req.path, path_len); if (decoded_len < 0) goto bad_request; req.path[decoded_len] = '\0'; if (memchr(req.path, '\r', (size_t)decoded_len) || memchr(req.path, '\n', (size_t)decoded_len)) goto bad_request; + if (query_start != NULL) { + if (query_len >= sizeof(req.query)) + goto bad_request; + if (memchr(query_start + 1, '\r', query_len) || + memchr(query_start + 1, '\n', query_len)) + goto bad_request; + memcpy(req.query, query_start + 1, query_len); + req.query[query_len] = '\0'; + } + /* Skip the HTTP version token; only its CRLF framing matters here. */ p = q + 1; q = strchr(p, '\r'); if (!q) goto bad_request; - n = q - p; - if (n >= sizeof(req.query)) - goto bad_request; - memcpy(req.query, p, n); - req.query[n] = '\0'; p = q + 2; /* Parse the headers */ From ee7043afb6b22ce741c0c0202c68313815fe14dd Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:42:56 -0700 Subject: [PATCH 07/11] Guard TFTP numeric option parse against 32-bit overflow --- src/tftp/wolftftp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tftp/wolftftp.c b/src/tftp/wolftftp.c index aa7d9ab8..2f18db92 100644 --- a/src/tftp/wolftftp.c +++ b/src/tftp/wolftftp.c @@ -94,13 +94,17 @@ static int wolftftp_parse_u32(const char *value, uint32_t max_value, uint32_t *out) { uint32_t v = 0; + uint32_t digit; if (value == NULL || out == NULL || *value == '\0') return -1; while (*value != '\0') { if (*value < '0' || *value > '9') return -1; - v = (v * 10U) + (uint32_t)(*value - '0'); + digit = (uint32_t)(*value - '0'); + if (v > (0xFFFFFFFFU - digit) / 10U) + return -1; + v = (v * 10U) + digit; if (v > max_value) return -1; value++; From 856818c2adb5eb7f806a76148d6ed9687d1d3f1b Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:42:56 -0700 Subject: [PATCH 08/11] Bound TFTP transfer mode parsing to received packet --- src/tftp/wolftftp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tftp/wolftftp.c b/src/tftp/wolftftp.c index 2f18db92..67effd75 100644 --- a/src/tftp/wolftftp.c +++ b/src/tftp/wolftftp.c @@ -339,7 +339,8 @@ static int wolftftp_parse_request(const uint8_t *buf, uint16_t len, return WOLFTFTP_ERR_PACKET; p += slen + 1U; slen = wolftftp_strnlen_local(p, (size_t)(buf + len - (const uint8_t *)p)); - if (slen == 0 || wolftftp_stricmp_local(p, "octet") != 0) + if (slen == 0 || (const uint8_t *)(p + slen) >= buf + len || + wolftftp_stricmp_local(p, "octet") != 0) return WOLFTFTP_ERR_UNSUPPORTED; p += slen + 1U; From b409fa060b0ca66288b8b57ab9839c9b9735a37c Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:45:32 -0700 Subject: [PATCH 09/11] Reuse socket on wolfMQTT async connect retry --- src/port/wolfmqtt_io.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/port/wolfmqtt_io.c b/src/port/wolfmqtt_io.c index c9abba20..d83ec97e 100644 --- a/src/port/wolfmqtt_io.c +++ b/src/port/wolfmqtt_io.c @@ -45,6 +45,7 @@ static struct wolfmqtt_io_desc *io_desc_alloc(void) if (!io_descs[i].in_use) { io_descs[i].in_use = 1; io_descs[i].connected = 0; + io_descs[i].fd = -1; return &io_descs[i]; } } @@ -75,10 +76,12 @@ static int wolfmqtt_net_connect(void *context, const char *host, word16 port, return MQTT_CODE_ERROR_BAD_ARG; } - /* Create TCP socket */ - desc->fd = wolfIP_sock_socket(desc->stack, AF_INET, IPSTACK_SOCK_STREAM, 0); + /* Create the socket only on the first attempt; async retries reuse it. */ if (desc->fd < 0) { - return MQTT_CODE_ERROR_NETWORK; + desc->fd = wolfIP_sock_socket(desc->stack, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (desc->fd < 0) { + return MQTT_CODE_ERROR_NETWORK; + } } /* Set up address */ From 7df7431330c9e76240367ad97b380ad37c3ef8ff Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:45:32 -0700 Subject: [PATCH 10/11] Honor non-blocking and timeout in POSIX recvmsg --- src/port/posix/bsd_socket.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/port/posix/bsd_socket.c b/src/port/posix/bsd_socket.c index a09632e9..60f1a2ab 100644 --- a/src/port/posix/bsd_socket.c +++ b/src/port/posix/bsd_socket.c @@ -1008,7 +1008,6 @@ int wolfIP_sock_recvmsg(struct wolfIP *ipstack, int sockfd, struct msghdr *msg, uint8_t stack_buf[WOLFIP_IOV_STACK_BUF] = {0}; uint8_t *heap_buf = NULL; uint8_t *buf = NULL; - struct pollfd pfd; if (wolfip_calc_msghdr_len(msg, &total_len) < 0) return -WOLFIP_EINVAL; @@ -1029,16 +1028,9 @@ int wolfIP_sock_recvmsg(struct wolfIP *ipstack, int sockfd, struct msghdr *msg, } } - pfd.fd = sockfd; - pfd.events = POLLIN; - pfd.revents = 0; - while (1) { - ret = wolfIP_sock_recvfrom(ipstack, sockfd, buf ? buf : msg->msg_iov[0].iov_base, - total_len, flags, src, src ? &addrlen : NULL); - if (ret != -WOLFIP_EAGAIN) - break; - (void)wolfIP_sock_poll(ipstack, &pfd, 1, -1); - } + /* Return EAGAIN to the caller; the wrapper handles non-blocking/timeout. */ + ret = wolfIP_sock_recvfrom(ipstack, sockfd, buf ? buf : msg->msg_iov[0].iov_base, + total_len, flags, src, src ? &addrlen : NULL); if (ret > 0 && msg->msg_iovlen > 1 && buf) { wolfip_scatter_iov(msg, buf, (size_t)ret); } From abf37510bada2d6247090a0cad5740de637a7c6c Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 15 Jun 2026 15:45:32 -0700 Subject: [PATCH 11/11] Fix Pico USB network buffer dimensions and bounds --- src/port/raspberry-pico-usb-server/src/main.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/port/raspberry-pico-usb-server/src/main.c b/src/port/raspberry-pico-usb-server/src/main.c index d4ee612b..fbf177c6 100644 --- a/src/port/raspberry-pico-usb-server/src/main.c +++ b/src/port/raspberry-pico-usb-server/src/main.c @@ -50,11 +50,11 @@ extern char MOTD[]; static struct wolfIP *IPStack = NULL; /* Two static buffers for RX frames from USB host */ -uint8_t tusb_net_rxbuf[LINK_MTU][2]; +uint8_t tusb_net_rxbuf[2][LINK_MTU]; uint8_t tusb_net_rxbuf_used[2] = {0, 0}; /* Two static buffers for TX frames to USB host */ -uint8_t tusb_net_txbuf[LINK_MTU][2]; +uint8_t tusb_net_txbuf[2][LINK_MTU]; uint16_t tusb_net_txbuf_sz[2] = {0, 0}; /* Fixed mac-address for the raspberry side of the link. @@ -81,6 +81,8 @@ static int ll_usb_send(struct wolfIP_ll_dev *dev, void *frame, uint32_t sz) { uint16_t sz16 = (uint16_t)sz; uint32_t i; (void) dev; + if (sz > LINK_MTU) + return 0; board_led_on(); for (;;) { if (!tud_ready()) { @@ -111,7 +113,7 @@ uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) { (void) ref; (void) arg; memcpy(dst, ref, arg); - if (ref == tusb_net_rxbuf[0]) + if (ref == tusb_net_txbuf[0]) tusb_net_txbuf_sz[0] = 0; else if (ref == tusb_net_txbuf[1]) tusb_net_txbuf_sz[1] = 0; @@ -127,6 +129,8 @@ uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) { static void tusb_net_push_rx(const uint8_t *src, uint16_t size) { uint8_t *dst = NULL; int i; + if (size > LINK_MTU) + return; for (i = 0; i < 2; i++) { if (!tusb_net_rxbuf_used[i]) { dst = tusb_net_rxbuf[i];