Explorar el Código

lwp: fix pid leak on exec failure before task startup

When msh tries to execute a non-ELF path, lwp_execve() may allocate a PID
before lwp_load() fails. The old error path only dropped the LWP reference,
leaving the PID tree entry pointing to a freed LWP.

In an init-less boot flow, this can poison pid 1 after a failed command from
msh. A later LWP launch may then treat the stale pid 1 entry as a valid parent
LWP, resulting in invalid pgrp/session state and a job-control assertion during
process exit.

Add lwp_pid_rollback() for exec/spawn failures before the process becomes
runnable. Unlike lwp_pid_put(), it always releases the PID lock and does not
enter the "no more pid allocation" state when the PID tree becomes empty.

Use the rollback helper in lwp_execve() failure paths after PID allocation.

Signed-off-by: zhangyang <gaoshanliukou@163.com>
yang.zhang hace 3 semanas
padre
commit
d4f4c50b9e
Se han modificado 3 ficheros con 34 adiciones y 4 borrados
  1. 4 4
      components/lwp/lwp.c
  2. 29 0
      components/lwp/lwp_pid.c
  3. 1 0
      components/lwp/lwp_pid.h

+ 4 - 4
components/lwp/lwp.c

@@ -514,14 +514,14 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
 
     if ((tid = lwp_tid_get()) == 0)
     {
-        lwp_ref_dec(lwp);
+        lwp_pid_rollback(lwp);
         return -ENOMEM;
     }
 #ifdef ARCH_MM_MMU
     if (lwp_user_space_init(lwp, 0) != 0)
     {
         lwp_tid_put(tid);
-        lwp_ref_dec(lwp);
+        lwp_pid_rollback(lwp);
         return -ENOMEM;
     }
 #endif
@@ -529,7 +529,7 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
     if ((aux = argscopy(lwp, argc, argv, envp)) == RT_NULL)
     {
         lwp_tid_put(tid);
-        lwp_ref_dec(lwp);
+        lwp_pid_rollback(lwp);
         return -ENOMEM;
     }
 
@@ -629,7 +629,7 @@ pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp)
     }
 
     lwp_tid_put(tid);
-    lwp_ref_dec(lwp);
+    lwp_pid_rollback(lwp);
 
     return -RT_ERROR;
 }

+ 29 - 0
components/lwp/lwp_pid.c

@@ -335,6 +335,35 @@ void lwp_pid_put(struct rt_lwp *lwp)
     lwp_ref_dec(lwp);
 }
 
+/**
+ * @brief Roll back a PID allocated for a process that never became runnable.
+ *
+ * @param[in,out] lwp The lightweight process whose PID allocation should be
+ *                    undone. The LWP must have been allocated a PID, but must
+ *                    not have been made visible as a runnable user task.
+ *
+ * @note This helper is intended for exec/spawn failure paths after PID
+ *       allocation and before task startup. It removes the PID table entry,
+ *       clears lwp->pid, and drops the initial LWP reference.
+ *
+ *       Do not use lwp_pid_put() for this case. lwp_pid_put() has process-exit
+ *       semantics: when the PID tree becomes empty it wakes waiters and keeps
+ *       the PID lock held to prevent new PID allocation. A failed exec rollback
+ *       must release the PID lock unconditionally so later LWP launches can
+ *       still allocate PIDs, especially in init-less boot flows.
+ */
+void lwp_pid_rollback(struct rt_lwp *lwp)
+{
+    _free_proc_dentry(lwp);
+
+    lwp_pid_lock_take();
+    lwp_pid_put_locked(lwp->pid);
+    lwp_pid_lock_release();
+
+    lwp->pid = 0;
+    lwp_ref_dec(lwp);
+}
+
 /**
  * @brief Set the LWP for a given PID while holding the PID lock
  *

+ 1 - 0
components/lwp/lwp_pid.h

@@ -29,6 +29,7 @@ int lwp_pid_init(void);
 int lwp_pid_wait_for_empty(int wait_flags, rt_tick_t to);
 int lwp_pid_for_each(int (*cb)(pid_t pid, void *data), void *data);
 void lwp_pid_put(struct rt_lwp *lwp);
+void lwp_pid_rollback(struct rt_lwp *lwp);
 void lwp_pid_lock_take(void);
 void lwp_pid_lock_release(void);