LWIP_UNUSED_ARG(pcb);
LWIP_ASSERT("conn != NULL", (conn != NULL));
+ /* Fundamental fix: Check if connection is still valid before processing any state.
+ * If pcb is NULL, it means err_tcp was called and the connection is closed.
+ * This can happen if poll_tcp was scheduled before err_tcp but executed after. */
+ if (conn->pcb.tcp == NULL) {
+ /* Connection was closed by err_tcp. Nothing to do. */
+ return ERR_OK;
+ }
+
if (conn->state == NETCONN_WRITE) {
- lwip_netconn_do_writemore(conn WRITE_DELAYED);
+ /* Only proceed if current_msg is valid. If it's NULL, the write operation
+ * was already completed or aborted, but state wasn't reset yet. */
+ if (conn->current_msg != NULL) {
+ lwip_netconn_do_writemore(conn WRITE_DELAYED);
+ }
} else if (conn->state == NETCONN_CLOSE) {
#if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
LWIP_ASSERT("conn != NULL", (conn != NULL));
if (conn) {
+ /* Fundamental fix: Check if connection is still valid before processing any state.
+ * If pcb is NULL, it means err_tcp was called and the connection is closed.
+ * This can happen if sent_tcp was scheduled before err_tcp but executed after. */
+ if (conn->pcb.tcp == NULL) {
+ /* Connection was closed by err_tcp. Nothing to do. */
+ return ERR_OK;
+ }
+
if (conn->state == NETCONN_WRITE) {
- lwip_netconn_do_writemore(conn WRITE_DELAYED);
+ /* Only proceed if current_msg is valid. If it's NULL, the write operation
+ * was already completed or aborted, but state wasn't reset yet. */
+ if (conn->current_msg != NULL) {
+ lwip_netconn_do_writemore(conn WRITE_DELAYED);
+ }
} else if (conn->state == NETCONN_CLOSE) {
lwip_netconn_do_close_internal(conn WRITE_DELAYED);
}
#endif /* LWIP_SO_SNDTIMEO */
{
do {
+ /* Runtime checks: verify pcb and vector are still valid before accessing them.
+ * These can become invalid if the connection was closed by another thread. */
+ if (conn->pcb.tcp == NULL || conn->current_msg == NULL ||
+ conn->current_msg->msg.w.vector == NULL) {
+ /* Connection was closed or message invalidated, abort write */
+ err = ERR_CONN;
+ write_finished = 1;
+ break;
+ }
+
dataptr = (const u8_t *)conn->current_msg->msg.w.vector->ptr + conn->current_msg->msg.w.vector_off;
diff = conn->current_msg->msg.w.vector->len - conn->current_msg->msg.w.vector_off;
if (diff > 0xffffUL) { /* max_u16_t */
} else {
len = (u16_t)diff;
}
+
+ /* Re-check pcb validity before calling tcp_sndbuf */
+ if (conn->pcb.tcp == NULL) {
+ err = ERR_CONN;
+ write_finished = 1;
+ break;
+ }
available = tcp_sndbuf(conn->pcb.tcp);
if (available < len) {
/* don't try to write more than sendbuf */
} else {
write_more = 0;
}
+ /* Re-check pcb validity before calling tcp_write */
+ if (conn->pcb.tcp == NULL) {
+ err = ERR_CONN;
+ write_finished = 1;
+ break;
+ }
err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
if (err == ERR_OK) {
conn->current_msg->msg.w.offset += len;
/* if OK or memory error, check available space */
if ((err == ERR_OK) || (err == ERR_MEM)) {
err_mem:
- if (dontblock && (conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len)) {
- /* non-blocking write did not write everything: mark the pcb non-writable
- and let poll_tcp check writable space to mark the pcb writable again */
- API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
- conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
- } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
- (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
- /* The queued byte- or pbuf-count exceeds the configured low-water limit,
- let select mark this pcb as non-writable. */
- API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
+ /* Re-check validity before accessing current_msg and pcb */
+ if (conn->current_msg != NULL && conn->pcb.tcp != NULL) {
+ if (dontblock && (conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len)) {
+ /* non-blocking write did not write everything: mark the pcb non-writable
+ and let poll_tcp check writable space to mark the pcb writable again */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
+ conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+ } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+ (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
+ /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+ let select mark this pcb as non-writable. */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
+ }
}
}
/* return sent length (caller reads length from msg.w.offset) */
write_finished = 1;
}
- out_err = tcp_output(conn->pcb.tcp);
- if (out_err == ERR_RTE) {
- /* If tcp_output fails because no route is found,
- don't try writing any more but return the error
- to the application thread. */
- err = out_err;
+ /* Re-check pcb validity before calling tcp_output */
+ if (conn->pcb.tcp == NULL) {
+ err = ERR_CONN;
write_finished = 1;
+ } else {
+ out_err = tcp_output(conn->pcb.tcp);
+ if (out_err == ERR_RTE) {
+ /* If tcp_output fails because no route is found,
+ don't try writing any more but return the error
+ to the application thread. */
+ err = out_err;
+ write_finished = 1;
+ }
}
} else if (err == ERR_MEM) {
/* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
will remain non-writable until sent_tcp/poll_tcp is called */
/* tcp_write returned ERR_MEM, try tcp_output anyway */
- err_t out_err = tcp_output(conn->pcb.tcp);
- if (out_err == ERR_RTE) {
- /* If tcp_output fails because no route is found,
- don't try writing any more but return the error
- to the application thread. */
- err = out_err;
- write_finished = 1;
- } else if (dontblock) {
- /* non-blocking write is done on ERR_MEM, set error according
- to partial write or not */
- err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
+ /* Re-check pcb validity before calling tcp_output */
+ if (conn->pcb.tcp == NULL) {
+ err = ERR_CONN;
write_finished = 1;
+ } else {
+ err_t out_err = tcp_output(conn->pcb.tcp);
+ if (out_err == ERR_RTE) {
+ /* If tcp_output fails because no route is found,
+ don't try writing any more but return the error
+ to the application thread. */
+ err = out_err;
+ write_finished = 1;
+ } else if (dontblock) {
+ /* non-blocking write is done on ERR_MEM, set error according
+ to partial write or not */
+ err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
+ write_finished = 1;
+ }
}
} else {
/* On errors != ERR_MEM, we don't try writing any more but return
and back to application task */
sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
conn->current_msg->err = err;
- conn->current_msg = NULL;
+ /* Reset state BEFORE clearing current_msg to avoid race condition:
+ * If poll_tcp/sent_tcp is called between these two assignments,
+ * we want state to be NETCONN_NONE (not NETCONN_WRITE) when current_msg is NULL */
conn->state = NETCONN_NONE;
+ conn->current_msg = NULL;
#if LWIP_TCPIP_CORE_LOCKING
if (delayed)
#endif