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
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ EXE=build/tcpecho build/tcp_netcat_poll build/tcp_netcat_select \
build/test-evloop build/test-dns build/test-wolfssl-forwarding \
build/test-ttl-expired build/test-wolfssl build/test-httpd \
build/test-http-smuggle build/test-http-arg-oob \
build/test-http-close-notify \
build/test-freertos-close-last-ack \
build/test-posix-errno \
build/ipfilter-logger \
build/test-esp build/esp-server
Expand Down Expand Up @@ -423,6 +425,22 @@ build/test-http-arg-oob: src/test/test_http_arg_oob.c src/http/httpd.c
@echo "[LD] $@"
@$(CC) $(CFLAGS) -o $@ src/test/test_http_arg_oob.c $(LDFLAGS) -lwolfssl

# Standalone regression test for TLS close_notify on every close path (F-5732).
# It #includes httpd.c directly and stubs the wolfSSL teardown calls to record
# their order, so it does not link the real wolfSSL library.
build/test-http-close-notify:CFLAGS+=-Wno-cpp -DWOLFSSL_DEBUG -DWOLFSSL_WOLFIP -DWOLFIP_ENABLE_HTTP -Isrc/http
build/test-http-close-notify: src/test/test_http_close_notify.c src/http/httpd.c
@mkdir -p build || true
@echo "[LD] $@"
@$(CC) $(CFLAGS) -o $@ src/test/test_http_close_notify.c $(LDFLAGS)

# Standalone regression test for the FreeRTOS BSD close() wrapper when
# CB_EVENT_CLOSED is delivered synchronously during LAST_ACK teardown.
build/test-freertos-close-last-ack: src/test/test_freertos_close_last_ack.c src/port/freeRTOS/bsd_socket.c
@mkdir -p build || true
@echo "[LD] $@"
@$(CC) -Isrc/test/freertos_mocks $(CFLAGS) -o $@ src/test/test_freertos_close_last_ack.c $(LDFLAGS)

build/%.o: src/%.c
@mkdir -p `dirname $@` || true
@echo "[CC] $<"
Expand Down
44 changes: 22 additions & 22 deletions src/http/httpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ static struct http_url *http_find_url(struct httpd *httpd, const char *path) {
return NULL;
}

static void http_close_client(struct http_client *hc);

void http_send_response_headers(struct http_client *hc, int status_code, const char *status_text, const char *content_type, size_t content_length)
{
char txt_response[HTTP_TX_BUF_LEN];
Expand Down Expand Up @@ -133,10 +135,7 @@ void http_send_response_headers(struct http_client *hc, int status_code, const c
}
if (rc <= 0) {
/* Error – close connection */
wolfSSL_free(hc->ssl);
hc->ssl = NULL;
wolfIP_sock_close(hc->httpd->ipstack, hc->client_sd);
hc->client_sd = 0;
http_close_client(hc);
}
}

Expand All @@ -150,10 +149,7 @@ void http_send_response_body(struct http_client *hc, const void *body, size_t le
rc = wolfIP_sock_send(hc->httpd->ipstack, hc->client_sd, body, len, 0);

if (rc <= 0) {
wolfSSL_free(hc->ssl);
hc->ssl = NULL;
wolfIP_sock_close(hc->httpd->ipstack, hc->client_sd);
hc->client_sd = 0;
http_close_client(hc);
}
}

Expand All @@ -169,6 +165,21 @@ static int http_write_response(struct http_client *hc, const void *buf, size_t l
return wolfIP_sock_send(s, hc->client_sd, buf, len, 0);
}

static void http_close_client(struct http_client *hc)
{
if (!hc)
return;

if (hc->ssl) {
wolfSSL_shutdown(hc->ssl);
wolfSSL_CleanupIO_wolfIP(hc->ssl);
wolfSSL_free(hc->ssl);
hc->ssl = NULL;
}
wolfIP_sock_close(hc->httpd->ipstack, hc->client_sd);
hc->client_sd = 0;
}

void http_send_response_chunk(struct http_client *hc, const void *chunk, size_t len) {
char txt_chunk[8];
memset(txt_chunk, 0, sizeof(txt_chunk));
Expand All @@ -178,10 +189,7 @@ void http_send_response_chunk(struct http_client *hc, const void *chunk, size_t
if ((http_write_response(hc, txt_chunk, strlen(txt_chunk)) <= 0) ||
(http_write_response(hc, chunk, len) <= 0) ||
(http_write_response(hc, "\r\n", 2) <= 0)) {
wolfSSL_free(hc->ssl);
hc->ssl = NULL;
wolfIP_sock_close(hc->httpd->ipstack, hc->client_sd);
hc->client_sd = 0;
http_close_client(hc);
}
}

Expand All @@ -194,10 +202,7 @@ void http_send_response_chunk_end(struct http_client *hc) {
else
rc = wolfIP_sock_send(hc->httpd->ipstack, hc->client_sd, "0\r\n\r\n", 5, 0);
if (rc <= 0) {
wolfSSL_free(hc->ssl);
hc->ssl = NULL;
wolfIP_sock_close(hc->httpd->ipstack, hc->client_sd);
hc->client_sd = 0;
http_close_client(hc);
}
}

Expand Down Expand Up @@ -483,19 +488,14 @@ static void http_recv_cb(int sd, uint16_t event, void *arg) {
return;

fail_close:
if (hc->ssl) {
wolfSSL_free(hc->ssl);
hc->ssl = NULL;
}
wolfIP_sock_close(hc->httpd->ipstack, sd);
http_close_client(hc);
/* wolfIP_sock_close on an ESTABLISHED socket only starts the active close
* (FIN_WAIT_1) and returns -EAGAIN; the socket lingers, still carrying this
* callback and its arg. Once we zero client_sd the slot is reused by the
* next accept, so the lingering socket's callback_arg would dangle onto a
* different live connection's state. Deregister it here so a late segment
* on the half-closed socket can no longer fire http_recv_cb. */
wolfIP_register_callback(hc->httpd->ipstack, sd, NULL, NULL);
hc->client_sd = 0;
}

static void http_accept_cb(int sd, uint16_t event, void *arg) {
Expand Down
16 changes: 16 additions & 0 deletions src/port/freeRTOS/bsd_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ typedef struct {
int internal_fd;
SemaphoreHandle_t ready_sem;
volatile uint16_t wait_events;
volatile uint16_t seen_events;
} wolfip_bsd_fd_entry;

static struct wolfIP *g_ipstack;
Expand Down Expand Up @@ -131,6 +132,7 @@ static int wolfip_bsd_fd_alloc(int internal_fd)
g_fds[i].internal_fd = internal_fd;
g_fds[i].ready_sem = sem;
g_fds[i].wait_events = 0;
g_fds[i].seen_events = 0;
return i;
}
}
Expand All @@ -148,6 +150,7 @@ static void wolfip_bsd_fd_free(int public_fd)
g_fds[public_fd].internal_fd = -1;
g_fds[public_fd].ready_sem = NULL;
g_fds[public_fd].wait_events = 0;
g_fds[public_fd].seen_events = 0;
}

static void wolfip_bsd_socket_cb(int internal_fd, uint16_t events, void *arg)
Expand All @@ -158,6 +161,7 @@ static void wolfip_bsd_socket_cb(int internal_fd, uint16_t events, void *arg)
if (entry == NULL) {
return;
}
entry->seen_events |= events;
g_cb_log_count++;
if ((events & CB_EVENT_CLOSED) != 0u || (g_cb_log_count & 0x1Fu) == 0u) {
printf("[sock_cb] ifd=%d events=0x%04x wait=0x%04x cb_count=%lu\n",
Expand All @@ -173,6 +177,7 @@ static void wolfip_bsd_socket_cb(int internal_fd, uint16_t events, void *arg)

static void wolfip_bsd_prepare_wait_locked(wolfip_bsd_fd_entry *entry, uint16_t wait_events)
{
entry->seen_events = 0;
entry->wait_events = wait_events;
while (xSemaphoreTake(entry->ready_sem, 0) == pdTRUE) {
}
Expand Down Expand Up @@ -655,6 +660,17 @@ int close(int sockfd)
xSemaphoreGive(g_lock);
return ret;
}
if ((ret == -1) && IS_SOCKET_TCP(entry->internal_fd) &&
((entry->seen_events & CB_EVENT_CLOSED) != 0u)) {
/* The TCP core can destroy the socket immediately after delivering
* CB_EVENT_CLOSED (e.g. final ACK in LAST_ACK), so the retry sees
* the already-zeroed descriptor and wolfIP_sock_close() returns -1.
* Treat that as a completed close and release the wrapper slot. */
wolfIP_register_callback(g_ipstack, entry->internal_fd, NULL, NULL);
wolfip_bsd_fd_free(sockfd);
xSemaphoreGive(g_lock);
return 0;
}
if (ret != -WOLFIP_EAGAIN) {
xSemaphoreGive(g_lock);
wolfip_bsd_set_error(ret);
Expand Down
11 changes: 10 additions & 1 deletion src/port/stm32h563/tls_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ static void tls_client_handle_data(tls_client_t *client, uint16_t event);
/* External functions from wolfssl_io.c */
extern int wolfSSL_SetIO_wolfIP_CTX(WOLFSSL_CTX *ctx, struct wolfIP *s);
extern int wolfSSL_SetIO_wolfIP(WOLFSSL *ssl, int fd);
extern void wolfSSL_CleanupIO_wolfIP(WOLFSSL *ssl);

/* Debug output helper */
static void debug_print(const char *msg)
Expand Down Expand Up @@ -237,6 +238,7 @@ static void tls_client_free(tls_client_t *client)
{
if (client->ssl) {
wolfSSL_shutdown(client->ssl);
wolfSSL_CleanupIO_wolfIP(client->ssl);
wolfSSL_free(client->ssl);
client->ssl = NULL;
}
Expand Down Expand Up @@ -319,7 +321,14 @@ static void tls_listen_cb(int fd, uint16_t event, void *arg)
}

/* Associate SSL with socket */
wolfSSL_SetIO_wolfIP(client->ssl, client_fd);
if (wolfSSL_SetIO_wolfIP(client->ssl, client_fd) != 0) {
debug_print("TLS: SetIO failed\n");
wolfSSL_free(client->ssl);
client->ssl = NULL;
wolfIP_sock_close(server.stack, client_fd);
client->state = TLS_CLIENT_STATE_FREE;
return;
}

client->fd = client_fd;
client->state = TLS_CLIENT_STATE_HANDSHAKE;
Expand Down
25 changes: 23 additions & 2 deletions src/port/stm32h753/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ static int https_status_handler(struct httpd *httpd, struct http_client *hc,
#define RNG_SR_CECS (1u << 1)
#define RNG_SR_SECS (1u << 2)

/* Cortex-M7 DWT cycle counter (used to seed the RNG fallback with runtime
* entropy so a degraded device does not emit a globally-identical sequence) */
#define DWT_CTRL (*(volatile uint32_t *)0xE0001000UL)
#define DWT_CYCCNT (*(volatile uint32_t *)0xE0001004UL)
#define DWT_CTRL_CYCCNTENA (1u << 0)
#define CM_DEMCR (*(volatile uint32_t *)0xE000EDFCUL)
#define CM_DEMCR_TRCENA (1u << 24)

/* USART3 for debug output (ST-Link VCP on NUCLEO-H753ZI: PD8=TX, PD9=RX) */
#define USART3_BASE 0x40004800UL
#define USART3_CR1 (*(volatile uint32_t *)(USART3_BASE + 0x00))
Expand Down Expand Up @@ -369,8 +377,21 @@ uint32_t wolfIP_getrandom(void)
uint32_t val;
if (rng_get_word(&val) == 0)
return val;
/* Fallback LFSR if HW RNG fails */
static uint32_t lfsr = 0x1A2B3C4DU;
/* HW RNG failed: fall back to an xorshift LFSR seeded from runtime state
* rather than a compile-time constant, so a degraded device does not emit
* the same globally-known sequence (and thus predictable TCP ISNs) on
* every unit. The DWT cycle counter (CPU clock, free running once the
* stack is up) and any residual RNG_DR bits vary per device and power-up.
* Not a cryptographic RNG. */
static uint32_t lfsr = 0u;
if (lfsr == 0u) {
CM_DEMCR |= CM_DEMCR_TRCENA;
DWT_CTRL |= DWT_CTRL_CYCCNTENA;
lfsr = DWT_CYCCNT ^ RNG_DR ^ 0x1A2B3C4DU;
if (lfsr == 0u)
lfsr = 0x1A2B3C4DU; /* LFSR seed must never be zero */
}
lfsr ^= DWT_CYCCNT; /* mix in timing jitter on each call */
lfsr ^= lfsr << 13;
lfsr ^= lfsr >> 17;
lfsr ^= lfsr << 5;
Expand Down
19 changes: 15 additions & 4 deletions src/port/va416xx/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,31 @@ static int client_fd = -1;
/* wolfIP random number generator (required by stack) */
/* ========================================================================= */

/* SysTick Current Value Register: 24-bit free-running down-counter reloaded
* every 1ms (reload = SYSCLK/1000 = 100000 at 100MHz), so each read yields
* ~17 bits of fast-varying timing jitter. */
#define SYST_CVR (*(volatile uint32_t *)0xE000E018UL)

uint32_t wolfIP_getrandom(void)
{
static uint32_t lfsr;
static int seeded = 0;

if (!seeded) {
/* Seed from boot time so ISNs and ephemeral ports vary per power-up.
* HAL_time_ms at first wolfIP call is typically 1-5 s into boot.
* Note: not cryptographically secure; suitable for embedded demo use. */
lfsr = (uint32_t)HAL_time_ms;
/* Seed from boot time mixed with the SysTick current value so the
* initial state is not confined to the trivially-enumerable boot
* window (HAL_time_ms at first wolfIP call is typically 1-5 s into
* boot). Note: not cryptographically secure; suitable for embedded
* demo use. */
lfsr = (uint32_t)HAL_time_ms ^ (SYST_CVR << 8);
if (lfsr == 0U)
lfsr = 0x1A2B3C4DU; /* LFSR must never be zero */
seeded = 1;
}
/* Mix in SysTick timing jitter each call so a single observed output
* cannot be inverted to predict subsequent ISNs/ports/xids (xorshift32
* on its own is bijective). */
lfsr ^= SYST_CVR;
lfsr ^= lfsr << 13;
lfsr ^= lfsr >> 17;
lfsr ^= lfsr << 5;
Expand Down
18 changes: 14 additions & 4 deletions src/port/wolfssh_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,15 @@ static int wolfssh_io_recv(WOLFSSH *ssh, void *buf, word32 sz, void *ctx)
}

ret = wolfIP_sock_recv(desc->stack, desc->fd, buf, (int)sz, 0);
if (ret == -WOLFIP_EAGAIN || ret == -1) {
/* Only -WOLFIP_EAGAIN means "would block" (no data queued yet). A -1 is
* the "not established" / torn-down case from wolfIP_sock_recvfrom (peer
* RST drove the socket to TCP_CLOSED) and must be reported as a fatal
* close, otherwise wolfSSH retries the dead connection forever and the
* SSH handshake state machine is wedged in KEY_EXCHANGE indefinitely. */
if (ret == -WOLFIP_EAGAIN) {
return WS_CBIO_ERR_WANT_READ;
}
if (ret == 0) {
if (ret == 0 || ret == -1) {
return WS_CBIO_ERR_CONN_CLOSE;
}
if (ret < 0) {
Expand All @@ -94,10 +99,15 @@ static int wolfssh_io_send(WOLFSSH *ssh, void *buf, word32 sz, void *ctx)
}

ret = wolfIP_sock_send(desc->stack, desc->fd, buf, (int)sz, 0);
if (ret == -WOLFIP_EAGAIN || ret == -1) {
/* Only -WOLFIP_EAGAIN means "would block" (TX buffer full, nothing
* queued). A -1 is the "not established" / torn-down case from
* wolfIP_sock_sendto (peer RST) and must be reported as a fatal close,
* otherwise wolfSSH retries the dead connection forever and its io_desc
* slot is never released. */
if (ret == -WOLFIP_EAGAIN) {
return WS_CBIO_ERR_WANT_WRITE;
}
if (ret == 0) {
if (ret == 0 || ret == -1) {
return WS_CBIO_ERR_CONN_CLOSE;
}
if (ret < 0) {
Expand Down
34 changes: 32 additions & 2 deletions src/port/wolfssl_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ static int wolfIP_io_recv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
return WOLFSSL_CBIO_ERR_GENERAL;

ret = wolfIP_sock_recv(desc->stack, desc->fd, buf, sz, 0);
if (ret == -WOLFIP_EAGAIN || ret == -1)
/* Only -WOLFIP_EAGAIN means "would block": wolfIP_sock_recvfrom returns it
* (via queue_pop) for an established socket with an empty RX queue. A -1 is
* the "not established" / torn-down case and must be reported as a fatal
* close, otherwise wolfSSL keeps retrying a dead connection forever and the
* owning session is never released. */
if (ret == -WOLFIP_EAGAIN)
return WOLFSSL_CBIO_ERR_WANT_READ;
if (ret <= 0)
return WOLFSSL_CBIO_ERR_CONN_CLOSE;
Expand All @@ -89,7 +94,11 @@ static int wolfIP_io_send(WOLFSSL* ssl, char* buf, int sz, void* ctx)
return WOLFSSL_CBIO_ERR_GENERAL;

ret = wolfIP_sock_send(desc->stack, desc->fd, buf, sz, 0);
if (ret == -WOLFIP_EAGAIN || ret == -1)
/* Only -WOLFIP_EAGAIN means "would block" (TX buffer full, nothing queued).
* A -1 is the "not established" / torn-down case from wolfIP_sock_sendto and
* must be reported as a fatal close, otherwise wolfSSL retries the dead
* connection forever and its session is never released. */
if (ret == -WOLFIP_EAGAIN)
return WOLFSSL_CBIO_ERR_WANT_WRITE;
if (ret <= 0)
return WOLFSSL_CBIO_ERR_CONN_CLOSE;
Expand Down Expand Up @@ -135,3 +144,24 @@ int wolfSSL_SetIO_wolfIP(WOLFSSL* ssl, int fd)
}
return -1;
}

/* Release the io_descs[] slot allocated by wolfSSL_SetIO_wolfIP() for this
* session. Must be called on every TLS teardown path (before wolfSSL_free)
* or the static pool leaks one slot per connection and is exhausted after
* MAX_WOLFIP_CTX sessions. */
void wolfSSL_CleanupIO_wolfIP(WOLFSSL* ssl)
{
struct wolfip_io_desc *desc;

if (!ssl)
return;

desc = (struct wolfip_io_desc *)wolfSSL_GetIOReadCtx(ssl);
if (!desc)
return;

wolfSSL_SetIOReadCtx(ssl, NULL);
wolfSSL_SetIOWriteCtx(ssl, NULL);
desc->fd = 0;
desc->stack = NULL;
}
Loading
Loading