release LN V1.0.32 修复lwip 网络恢复时候的不稳定。
author孙春阳 <snickersun@163.com>
Wed, 31 Dec 2025 04:35:16 +0000 (12:35 +0800)
committer孙春阳 <snickersun@163.com>
Wed, 31 Dec 2025 04:35:16 +0000 (12:35 +0800)
autobuild.sh
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/api/api_msg.c

index 9a776ed..3a8b868 100644 (file)
@@ -59,7 +59,7 @@ declare -A PROJECT_VERSIONS=(
     ["w9"]="1.3.107" 
     ["lt22"]="1.3.107"
     ["n9"]="1.3.113"
-    ["ln"]="1.0.31"
+    ["ln"]="1.0.32"
     ["lnppp"]="1.1.1"
     ["n9ln"]="1.0.0" 
     
index fe1d4a3..2a83cfa 100644 (file)
@@ -376,12 +376,20 @@ poll_tcp(void *arg, struct tcp_pcb *pcb)
       lwip_netconn_do_writemore(conn  WRITE_DELAYED);
     }
   } else if (conn->state == NETCONN_CLOSE) {
+    /* Before calling lwip_netconn_do_close_internal, check if pcb and current_msg are valid.
+     * If either is NULL, it means the connection was closed (err_tcp was called).
+     * err_tcp should have already set state to NETCONN_NONE, but if there's a race condition,
+     * we just skip processing (defensive check, not state change). */
+    if (conn->pcb.tcp == NULL || conn->current_msg == NULL) {
+      /* Connection was closed by err_tcp. Nothing to do (defensive check only). */
+    } else {
 #if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
-    if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
-      conn->current_msg->msg.sd.polls_left--;
-    }
+      if (conn->current_msg->msg.sd.polls_left) {
+        conn->current_msg->msg.sd.polls_left--;
+      }
 #endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
-    lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+      lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+    }
   }
   /* @todo: implement connect timeout here? */
 
@@ -430,7 +438,15 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
         lwip_netconn_do_writemore(conn  WRITE_DELAYED);
       }
     } else if (conn->state == NETCONN_CLOSE) {
-      lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+      /* Before calling lwip_netconn_do_close_internal, check if pcb and current_msg are valid.
+       * If either is NULL, it means the connection was closed (err_tcp was called).
+       * err_tcp should have already set state to NETCONN_NONE, but if there's a race condition,
+       * we just skip processing (defensive check, not state change). */
+      if (conn->pcb.tcp == NULL || conn->current_msg == NULL) {
+        /* Connection was closed by err_tcp. Nothing to do (defensive check only). */
+      } else {
+        lwip_netconn_do_close_internal(conn  WRITE_DELAYED);
+      }
     }
 
     /* If the queued byte- or pbuf-count drops below the configured low-water limit,
@@ -458,6 +474,8 @@ err_tcp(void *arg, err_t err)
   struct netconn *conn;
   enum netconn_state old_state;
   void *mbox_msg;
+  sys_sem_t *op_completed_sem = NULL;
+  int need_signal = 0;
   SYS_ARCH_DECL_PROTECT(lev);
 
   conn = (struct netconn *)arg;
@@ -476,6 +494,34 @@ err_tcp(void *arg, err_t err)
   old_state = conn->state;
   conn->state = NETCONN_NONE;
 
+  /* Fix: Protect current_msg cleanup within the lock to prevent race condition
+   * with lwip_netconn_do_writemore() which may be accessing current_msg concurrently */
+  if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
+      (old_state == NETCONN_CONNECT)) {
+    int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
+    SET_NONBLOCKING_CONNECT(conn, 0);
+
+    if (!was_nonblocking_connect) {
+      /* set error return code - do this within lock protection */
+      if (conn->current_msg != NULL) {
+        if (old_state == NETCONN_CLOSE) {
+          /* let close succeed: the connection is closed after all... */
+          conn->current_msg->err = ERR_OK;
+        } else {
+          /* Write and connect fail */
+          conn->current_msg->err = err;
+        }
+        op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+        LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem));
+        /* Clear current_msg within lock protection to prevent race condition */
+        conn->current_msg = NULL;
+        need_signal = 1;
+      }
+    }
+  } else {
+    LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+  }
+
   SYS_ARCH_UNPROTECT(lev);
 
   /* Notify the user layer about a connection error. Used to signal select. */
@@ -497,34 +543,9 @@ err_tcp(void *arg, err_t err)
     sys_mbox_trypost(&conn->acceptmbox, mbox_msg);
   }
 
-  if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
-      (old_state == NETCONN_CONNECT)) {
-    /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
-       since the pcb has already been deleted! */
-    int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
-    SET_NONBLOCKING_CONNECT(conn, 0);
-
-    if (!was_nonblocking_connect) {
-      sys_sem_t *op_completed_sem;
-      /* set error return code */
-      LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
-      if (old_state == NETCONN_CLOSE) {
-        /* let close succeed: the connection is closed after all... */
-        conn->current_msg->err = ERR_OK;
-      } else {
-        /* Write and connect fail */
-        conn->current_msg->err = err;
-      }
-      op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
-      LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem));
-      conn->current_msg = NULL;
-      /* wake up the waiting task */
-      sys_sem_signal(op_completed_sem);
-    } else {
-      /* @todo: test what happens for error on nonblocking connect */
-    }
-  } else {
-    LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+  /* Wake up the waiting task outside of lock to avoid potential deadlock */
+  if (need_signal && op_completed_sem != NULL) {
+    sys_sem_signal(op_completed_sem);
   }
 }
 
@@ -1708,6 +1729,14 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
   LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
   LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
   LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+  
+  /* Defensive check: verify pcb and current_msg are still valid before accessing them.
+   * These can become invalid if err_tcp was called between poll_tcp's check and this function. */
+  if (conn->pcb.tcp == NULL || conn->current_msg == NULL || conn->state != NETCONN_WRITE) {
+    /* Connection was closed by err_tcp or state changed. Return error immediately. */
+    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);
@@ -1716,7 +1745,8 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
   dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
 
 #if LWIP_SO_SNDTIMEO
-  if ((conn->send_timeout != 0) &&
+  /* 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) {
@@ -1762,6 +1792,12 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
         if (dontblock) {
           if (!len) {
             /* set error according to partial write or not */
+            /* Re-check current_msg validity before accessing it */
+            if (conn->current_msg == NULL) {
+              err = ERR_CONN;
+              write_finished = 1;
+              break;
+            }
             err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
             goto err_mem;
           }
@@ -1769,6 +1805,12 @@ lwip_netconn_do_writemore(struct netconn *conn  WRITE_DELAYED_PARAM)
           apiflags |= TCP_WRITE_FLAG_MORE;
         }
       }
+      /* Re-check current_msg and vector validity before accessing them */
+      if (conn->current_msg == NULL || conn->current_msg->msg.w.vector == NULL) {
+        err = ERR_CONN;
+        write_finished = 1;
+        break;
+      }
       LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!",
                   ((conn->current_msg->msg.w.vector_off + len) <= conn->current_msg->msg.w.vector->len));
       /* we should loop around for more sending in the following cases:
@@ -1790,9 +1832,21 @@ 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) {
+          err = ERR_CONN;
+          write_finished = 1;
+          break;
+        }
         conn->current_msg->msg.w.offset += len;
         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) {
+          err = ERR_CONN;
+          write_finished = 1;
+          break;
+        }
         if (conn->current_msg->msg.w.vector_off == conn->current_msg->msg.w.vector->len) {
           conn->current_msg->msg.w.vector_cnt--;
           /* if we have additional vectors, move on to them */
@@ -1824,23 +1878,30 @@ err_mem:
 
     if (err == ERR_OK) {
       err_t out_err;
-      if ((conn->current_msg->msg.w.offset == conn->current_msg->msg.w.len) || dontblock) {
-        /* return sent length (caller reads length from msg.w.offset) */
-        write_finished = 1;
-      }
-      /* Re-check pcb validity before calling tcp_output */
-      if (conn->pcb.tcp == NULL) {
+      /* Re-check current_msg and pcb validity before accessing them */
+      if (conn->current_msg == NULL || conn->pcb.tcp == NULL) {
+        /* Connection was closed or message invalidated during write */
         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;
+        if ((conn->current_msg->msg.w.offset == conn->current_msg->msg.w.len) || dontblock) {
+          /* return sent length (caller reads length from msg.w.offset) */
           write_finished = 1;
         }
+        /* 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.
@@ -1864,8 +1925,14 @@ err_mem:
         } 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;
+          /* Re-check current_msg validity before accessing it */
+          if (conn->current_msg == NULL) {
+            err = ERR_CONN;
+            write_finished = 1;
+          } else {
+            err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
+            write_finished = 1;
+          }
         }
       }
     } else {
@@ -1877,18 +1944,27 @@ err_mem:
   if (write_finished) {
     /* everything was written: set back connection state
        and back to application task */
-    sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
-    conn->current_msg->err = err;
-    /* 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;
+    /* Re-check current_msg validity before accessing it (err_tcp may have cleared it) */
+    if (conn->current_msg != NULL) {
+      /* Normal cleanup: current_msg is still valid, so we need to clean up */
+      sys_sem_t *op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+      conn->current_msg->err = err;
+      /* 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)
+      if (delayed)
 #endif
-    {
-      sys_sem_signal(op_completed_sem);
+      {
+        sys_sem_signal(op_completed_sem);
+      }
+    } else {
+      /* current_msg was already cleared by err_tcp.
+       * err_tcp should have already set state to NETCONN_NONE.
+       * This is a defensive check: we don't modify state here,
+       * we just skip cleanup (defensive check, not state change). */
     }
   }
 #if LWIP_TCPIP_CORE_LOCKING