Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/pico_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ int pico_socket_getpeername(struct pico_socket *s, void *remote_addr, uint16_t *

int pico_socket_connect(struct pico_socket *s, const void *remote_addr, uint16_t remote_port);
int pico_socket_listen(struct pico_socket *s, const int backlog);
/* `orig` must point to a buffer large enough for the peer address of the
* socket's address family: sizeof(struct pico_ip4) for IPv4, sizeof(struct
* pico_ip6) for IPv6. Use `union pico_address` to be safe for either family;
* a smaller buffer is overflown by the family-sized copy (FSCT-2026-0018). */
struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *port);
int8_t pico_socket_del(struct pico_socket *s);

Expand Down
16 changes: 10 additions & 6 deletions modules/pico_dns_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,18 @@ static struct pico_dns_query *pico_dns_client_find_query(struct pico_stack *S, u
return NULL;
}

/* seek end of string */
static char *pico_dns_client_seek(char *ptr)
/* seek end of string, without reading past the end of the response buffer */
static char *pico_dns_client_seek(char *ptr, const char *end)
{
if (!ptr)
if (!ptr || !end)
return NULL;

while (*ptr != 0)
while ((ptr < end) && (*ptr != 0))
ptr++;
/* No terminator within the response: reject instead of walking past it. */
if (ptr >= end)
return NULL;

return ptr + 1;
}

Expand Down Expand Up @@ -358,7 +362,7 @@ static char *pico_dns_client_seek_suffix(char *suf, struct pico_dns_header *pre,

case PICO_DNS_LABEL:
dns_dbg("DNS: label\n");
suf = pico_dns_client_seek(suf);
suf = pico_dns_client_seek(suf, (const char *)pre + PICO_IP_MRU);
break;

default:
Expand Down Expand Up @@ -602,7 +606,7 @@ static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s)

/* FIX: what if the query is not a PTR query? */
domain = (char *)header + sizeof(struct pico_dns_header);
qsuffix = (struct pico_dns_question_suffix *)pico_dns_client_seek(domain);
qsuffix = (struct pico_dns_question_suffix *)pico_dns_client_seek(domain, dns_response + sizeof(dns_response));
/* valid asuffix is determined dynamically later on */

if (pico_dns_client_check_qsuffix(qsuffix, q) < 0)
Expand Down
8 changes: 8 additions & 0 deletions modules/pico_fragments.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,14 @@ static int pico_fragments_reassemble(struct pico_tree *tree, unsigned int len, u
return -3;
}

/* A reassembled datagram must fit the 16-bit frame length. Without this
* check the (uint16_t) cast below truncates the allocation while the copy
* loop writes the full length, overflowing the heap buffer. The fragments
* are left for the expiration timer, as on allocation failure below. */
if (len > (65535U - header_length)) {
return -4;
}

full = pico_frame_alloc((uint16_t)(header_length + len));
if (full) {
full->net_hdr = full->buffer;
Expand Down
6 changes: 5 additions & 1 deletion modules/pico_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,11 @@ static int pico_ipv4_process_in(struct pico_stack *S, struct pico_protocol *self
f->frag = short_be(hdr->frag);
#endif

if (f->transport_len > max_allowed) {
/* max_allowed measures the space after the fixed IP header, but the transport
* area starts option_len bytes further in; subtract the options so the check
* cannot admit a transport region running past the frame (FSCT-2026-0022).
* The net_len fit-check above guarantees max_allowed >= option_len. */
if (f->transport_len > (uint16_t)(max_allowed - option_len)) {
pico_frame_discard(f);
return 0; /* Packet is discarded due to unfeasible length */
}
Expand Down
10 changes: 9 additions & 1 deletion modules/pico_udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,15 @@ uint16_t pico_udp_recv(struct pico_socket *s, void *buf, uint16_t len, void *src
if (f) {
if (!f->payload_len) {
f->payload = f->transport_hdr + sizeof(struct pico_udp_hdr);
f->payload_len = (uint16_t)(f->transport_len - sizeof(struct pico_udp_hdr));
/* A datagram whose transport length is smaller than the UDP header
* carries no payload; without this guard the subtraction underflows
* to ~65528 and the copy below reads far past the frame
* (FSCT-2026-0019). */
if (f->transport_len < sizeof(struct pico_udp_hdr)) {
f->payload_len = 0;
} else {
f->payload_len = (uint16_t)(f->transport_len - sizeof(struct pico_udp_hdr));
}
}

udp_dbg("expected: %d, got: %d\n", len, f->payload_len);
Expand Down
8 changes: 6 additions & 2 deletions test/examples/tcpecho.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ void cb_tcpecho(uint16_t ev, struct pico_socket *s)
struct pico_socket *sock_a = {
0
};
struct pico_ip4 orig = {
/* Family-agnostic buffer: pico_socket_accept() copies the peer address
* sized by the socket's family (16 bytes for IPv6), so a bare
* struct pico_ip4 here would be overflowed by an IPv6 peer
* (FSCT-2026-0018). */
union pico_address orig = {
0
};
uint16_t port = 0;
Expand All @@ -68,7 +72,7 @@ void cb_tcpecho(uint16_t ev, struct pico_socket *s)
int yes = 1;

sock_a = pico_socket_accept(s, &orig, &port);
pico_ipv4_to_string(peer, orig.addr);
pico_ipv4_to_string(peer, orig.ip4.addr);
printf("Connection established with %s:%d.\n", peer, short_be(port));
pico_socket_setoption(sock_a, PICO_TCP_NODELAY, &yes);
/* Set keepalive options */
Expand Down
Loading