优化lwip tcp发送,降低无网络发送你数据造成的死机问题 3027/1
author孙春阳 <snickersun@163.com>
Tue, 6 Jan 2026 10:31:57 +0000 (18:31 +0800)
committer孙春阳 <snickersun@163.com>
Tue, 6 Jan 2026 10:31:57 +0000 (18:31 +0800)
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/api/api_msg.c

index 8bef06c..6f7216c 100644 (file)
@@ -364,7 +364,7 @@ poll_tcp(void *arg, struct tcp_pcb *pcb)
   /* 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) {
+  if (conn->pcb.tcp == NULL || conn->state == NETCONN_NONE) {
     /* Connection was closed by err_tcp. Nothing to do. */
     return ERR_OK;
   }
@@ -372,7 +372,8 @@ poll_tcp(void *arg, struct tcp_pcb *pcb)
   if (conn->state == NETCONN_WRITE) {
     /* 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) {
+    /* Re-check state and current_msg before calling lwip_netconn_do_writemore */
+    if (conn->current_msg != NULL && conn->state == NETCONN_WRITE && conn->pcb.tcp != NULL) {
       lwip_netconn_do_writemore(conn  WRITE_DELAYED);
     }
   } else if (conn->state == NETCONN_CLOSE) {
@@ -434,7 +435,8 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
     if (conn->state == NETCONN_WRITE) {
       /* 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) {
+      /* Re-check state and current_msg before calling lwip_netconn_do_writemore */
+      if (conn->current_msg != NULL && conn->state == NETCONN_WRITE && conn->pcb.tcp != NULL) {
         lwip_netconn_do_writemore(conn  WRITE_DELAYED);
       }
     } else if (conn->state == NETCONN_CLOSE) {
@@ -1737,26 +1739,45 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
     return ERR_CONN;
   }
   
+  /* Re-check before accessing current_msg members (err_tcp may have cleared it) */
+  if (conn->current_msg == NULL || conn->state != NETCONN_WRITE) {
+    return ERR_CONN;
+  }
+  
   LWIP_ASSERT("conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len",
               conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len);
   LWIP_ASSERT("conn->current_msg->msg.w.vector_cnt > 0", conn->current_msg->msg.w.vector_cnt > 0);
 
+  /* Save apiflags immediately after check to minimize race window */
+  if (conn->current_msg == NULL || conn->state != NETCONN_WRITE) {
+    return ERR_CONN;
+  }
   apiflags = conn->current_msg->msg.w.apiflags;
   dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
 
 #if LWIP_SO_SNDTIMEO
   /* Re-check current_msg validity before accessing timeout fields */
-  if (conn->current_msg != NULL && (conn->send_timeout != 0) &&
-      ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
-    write_finished = 1;
-    if (conn->current_msg->msg.w.offset == 0) {
-      /* nothing has been written */
-      err = ERR_WOULDBLOCK;
-    } else {
-      /* partial write */
-      err = ERR_OK;
+  if (conn->current_msg != NULL && conn->state == NETCONN_WRITE && (conn->send_timeout != 0)) {
+    /* Re-check again before accessing time_started (err_tcp may have cleared current_msg) */
+    if (conn->current_msg != NULL && conn->state == NETCONN_WRITE &&
+        ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
+      write_finished = 1;
+      /* Re-check before accessing offset */
+      if (conn->current_msg != NULL && conn->state == NETCONN_WRITE) {
+        if (conn->current_msg->msg.w.offset == 0) {
+          /* nothing has been written */
+          err = ERR_WOULDBLOCK;
+        } else {
+          /* partial write */
+          err = ERR_OK;
+        }
+      } else {
+        err = ERR_CONN;
+        write_finished = 1;
+      }
     }
-  } else
+  }
+  if (!write_finished)
 #endif /* LWIP_SO_SNDTIMEO */
   {
     do {
@@ -1806,7 +1827,7 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
         }
       }
       /* Re-check current_msg and vector validity before accessing them */
-      if (conn->current_msg == NULL || conn->current_msg->msg.w.vector == NULL) {
+      if (conn->current_msg == NULL || conn->current_msg->msg.w.vector == NULL || conn->state != NETCONN_WRITE) {
         err = ERR_CONN;
         write_finished = 1;
         break;
@@ -1817,6 +1838,12 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
            1) We couldn't finish the current vector because of 16-bit size limitations.
               tcp_write() and tcp_sndbuf() both are limited to 16-bit sizes
            2) We are sending the remainder of the current vector and have more */
+      /* Re-check before accessing vector_cnt */
+      if (conn->current_msg == NULL || conn->state != NETCONN_WRITE) {
+        err = ERR_CONN;
+        write_finished = 1;
+        break;
+      }
       if ((len == 0xffff && diff > 0xffffUL) ||
           (len == (u16_t)diff && conn->current_msg->msg.w.vector_cnt > 1)) {
         write_more = 1;
@@ -1833,7 +1860,7 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
       err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
       if (err == ERR_OK) {
         /* Re-check current_msg validity after tcp_write (err_tcp may have been called) */
-        if (conn->current_msg == NULL) {
+        if (conn->current_msg == NULL || conn->state != NETCONN_WRITE) {
           err = ERR_CONN;
           write_finished = 1;
           break;
@@ -1842,15 +1869,27 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
         conn->current_msg->msg.w.vector_off += len;
         /* check if current vector is finished */
         /* Re-check current_msg and vector validity before accessing vector->len */
-        if (conn->current_msg == NULL || conn->current_msg->msg.w.vector == NULL) {
+        if (conn->current_msg == NULL || conn->current_msg->msg.w.vector == NULL || conn->state != NETCONN_WRITE) {
           err = ERR_CONN;
           write_finished = 1;
           break;
         }
         if (conn->current_msg->msg.w.vector_off == conn->current_msg->msg.w.vector->len) {
+          /* Re-check before modifying vector_cnt (err_tcp may have cleared current_msg) */
+          if (conn->current_msg == NULL || conn->state != NETCONN_WRITE) {
+            err = ERR_CONN;
+            write_finished = 1;
+            break;
+          }
           conn->current_msg->msg.w.vector_cnt--;
           /* if we have additional vectors, move on to them */
           if (conn->current_msg->msg.w.vector_cnt > 0) {
+            /* Re-check before modifying vector pointer (err_tcp may have cleared current_msg) */
+            if (conn->current_msg == NULL || conn->state != NETCONN_WRITE) {
+              err = ERR_CONN;
+              write_finished = 1;
+              break;
+            }
             conn->current_msg->msg.w.vector++;
             conn->current_msg->msg.w.vector_off = 0;
           }
@@ -1861,8 +1900,11 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
     if ((err == ERR_OK) || (err == ERR_MEM)) {
 err_mem:
       /* 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)) {
+      if (conn->current_msg != NULL && conn->pcb.tcp != NULL && conn->state == NETCONN_WRITE) {
+        /* Re-check again before accessing current_msg members */
+        if (conn->current_msg == NULL || conn->state != NETCONN_WRITE) {
+          /* Skip this block if current_msg was cleared */
+        } else 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);