lwip增加安全防护,防止断网恢复异常死机 3001/1 3001/head 3002/target/1
author孙春阳 <snickersun@163.com>
Tue, 30 Dec 2025 08:50:28 +0000 (16:50 +0800)
committer孙春阳 <snickersun@163.com>
Tue, 30 Dec 2025 08:50:28 +0000 (16:50 +0800)
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/api/api_msg.c

index ab47f0d..fe1d4a3 100644 (file)
@@ -361,8 +361,20 @@ poll_tcp(void *arg, struct tcp_pcb *pcb)
   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) {
@@ -403,8 +415,20 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
   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);
     }
@@ -1706,6 +1730,16 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
 #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 */
@@ -1714,6 +1748,13 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
       } 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 */
@@ -1741,6 +1782,12 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
       } 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;
@@ -1759,16 +1806,19 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
     /* 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);
+        }
       }
     }
 
@@ -1778,13 +1828,19 @@ err_mem:
         /* 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.
@@ -1793,18 +1849,24 @@ err_mem:
          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
@@ -1817,8 +1879,11 @@ err_mem:
        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