summaryrefslogtreecommitdiff
path: root/components/lwp/lwp_pid.c
diff options
context:
space:
mode:
Diffstat (limited to 'components/lwp/lwp_pid.c')
-rw-r--r--components/lwp/lwp_pid.c380
1 files changed, 234 insertions, 146 deletions
diff --git a/components/lwp/lwp_pid.c b/components/lwp/lwp_pid.c
index 2262a33f0f..cee21aeca9 100644
--- a/components/lwp/lwp_pid.c
+++ b/components/lwp/lwp_pid.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
+ * Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
@@ -9,30 +9,31 @@
* 2021-02-20 lizhirui fix warning
* 2023-06-26 shell clear ref to parent on waitpid()
* Remove recycling of lwp on waitpid() and leave it to defunct routine
+ * 2023-07-27 shell Move the detach of children process on parent exit to lwp_terminate.
+ * Make lwp_from_pid locked by caller to avoid possible use-after-free
+ * error
*/
#include <rthw.h>
#include <rtthread.h>
+#define DBG_TAG "lwp.pid"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
#include <dfs_file.h>
#include <unistd.h>
#include <stdio.h> /* rename() */
#include <sys/stat.h>
#include <sys/statfs.h> /* statfs() */
-#include "lwp.h"
-#include "lwp_pid.h"
-#include "lwp_signal.h"
+#include "lwp_internal.h"
#include "tty.h"
#ifdef ARCH_MM_MMU
#include "lwp_user_mm.h"
#endif
-#define DBG_TAG "LWP_PID"
-#define DBG_LVL DBG_INFO
-#include <rtdbg.h>
-
#define PID_MAX 10000
#define PID_CT_ASSERT(name, x) \
@@ -46,19 +47,40 @@ static struct lwp_avl_struct *lwp_pid_free_head = RT_NULL;
static int lwp_pid_ary_alloced = 0;
static struct lwp_avl_struct *lwp_pid_root = RT_NULL;
static pid_t current_pid = 0;
+static struct rt_mutex pid_mtx;
+
+int lwp_pid_init(void)
+{
+ rt_mutex_init(&pid_mtx, "pidmtx", RT_IPC_FLAG_PRIO);
+ return 0;
+}
+
+void lwp_pid_lock_take(void)
+{
+ DEF_RETURN_CODE(rc);
+
+ rc = lwp_mutex_take_safe(&pid_mtx, RT_WAITING_FOREVER, 0);
+ /* should never failed */
+ RT_ASSERT(rc == RT_EOK);
+}
+
+void lwp_pid_lock_release(void)
+{
+ /* should never failed */
+ if (lwp_mutex_release_safe(&pid_mtx) != RT_EOK)
+ RT_ASSERT(0);
+}
struct lwp_avl_struct *lwp_get_pid_ary(void)
{
return lwp_pid_ary;
}
-static pid_t lwp_pid_get(void)
+static pid_t lwp_pid_get_locked(void)
{
- rt_base_t level;
struct lwp_avl_struct *p;
pid_t pid = 0;
- level = rt_hw_interrupt_disable();
p = lwp_pid_free_head;
if (p)
{
@@ -97,13 +119,11 @@ static pid_t lwp_pid_get(void)
lwp_avl_insert(p, &lwp_pid_root);
current_pid = pid;
}
- rt_hw_interrupt_enable(level);
return pid;
}
-static void lwp_pid_put(pid_t pid)
+static void lwp_pid_put_locked(pid_t pid)
{
- rt_base_t level;
struct lwp_avl_struct *p;
if (pid == 0)
@@ -111,7 +131,6 @@ static void lwp_pid_put(pid_t pid)
return;
}
- level = rt_hw_interrupt_disable();
p = lwp_avl_find(pid, lwp_pid_root);
if (p)
{
@@ -120,21 +139,27 @@ static void lwp_pid_put(pid_t pid)
p->avl_right = lwp_pid_free_head;
lwp_pid_free_head = p;
}
- rt_hw_interrupt_enable(level);
}
-static void lwp_pid_set_lwp(pid_t pid, struct rt_lwp *lwp)
+void lwp_pid_put(struct rt_lwp *lwp)
+{
+ lwp_pid_lock_take();
+ lwp_pid_put_locked(lwp->pid);
+ lwp_pid_lock_release();
+
+ /* reset pid field */
+ lwp->pid = 0;
+}
+
+static void lwp_pid_set_lwp_locked(pid_t pid, struct rt_lwp *lwp)
{
- rt_base_t level;
struct lwp_avl_struct *p;
- level = rt_hw_interrupt_disable();
p = lwp_avl_find(pid, lwp_pid_root);
if (p)
{
p->data = lwp;
}
- rt_hw_interrupt_enable(level);
}
static void __exit_files(struct rt_lwp *lwp)
@@ -203,11 +228,7 @@ int lwp_user_object_add(struct rt_lwp *lwp, rt_object_t object)
node = (struct lwp_avl_struct *)rt_malloc(sizeof(struct lwp_avl_struct));
if (node)
{
- rt_base_t level;
-
- level = rt_hw_interrupt_disable();
- object->lwp_ref_count++;
- rt_hw_interrupt_enable(level);
+ rt_atomic_add(&object->lwp_ref_count, 1);
node->avl_key = (avl_key_t)object;
lwp_avl_insert(node, &lwp->object_root);
ret = 0;
@@ -317,7 +338,6 @@ void lwp_user_object_dup(struct rt_lwp *dst_lwp, struct rt_lwp *src_lwp)
rt_lwp_t lwp_create(rt_base_t flags)
{
pid_t pid;
- rt_base_t level;
rt_lwp_t new_lwp = rt_calloc(1, sizeof(struct rt_lwp));
if (new_lwp)
@@ -336,8 +356,8 @@ rt_lwp_t lwp_create(rt_base_t flags)
/* lwp with pid */
if (flags & LWP_CREATE_FLAG_ALLOC_PID)
{
- level = rt_hw_interrupt_disable();
- pid = lwp_pid_get();
+ lwp_pid_lock_take();
+ pid = lwp_pid_get_locked();
if (pid == 0)
{
lwp_user_object_lock_destroy(new_lwp);
@@ -348,29 +368,33 @@ rt_lwp_t lwp_create(rt_base_t flags)
else
{
new_lwp->pid = pid;
- lwp_pid_set_lwp(pid, new_lwp);
+ lwp_pid_set_lwp_locked(pid, new_lwp);
}
-
- rt_hw_interrupt_enable(level);
+ lwp_pid_lock_release();
}
}
+
+ LOG_D("%s(pid=%d) => %p", __func__, new_lwp->pid, new_lwp);
return new_lwp;
}
+/** when reference is 0, a lwp can be released */
void lwp_free(struct rt_lwp* lwp)
{
- rt_base_t level;
-
if (lwp == RT_NULL)
{
return;
}
+ /**
+ * Brief: Recycle the lwp when reference is cleared
+ *
+ * Note: Critical Section
+ * - lwp (RW. there is no other writer/reader compete with lwp_free, since
+ * all the reference is clear)
+ */
LOG_D("lwp free: %p\n", lwp);
- level = rt_hw_interrupt_disable();
- lwp->finish = 1;
- rt_hw_interrupt_enable(level);
if (lwp->args != RT_NULL)
{
@@ -429,8 +453,6 @@ void lwp_free(struct rt_lwp* lwp)
lwp_unmap_user_space(lwp);
#endif
timer_list_free(&lwp->timer);
-
- level = rt_hw_interrupt_disable();
/* for children */
while (lwp->first_child)
{
@@ -438,21 +460,20 @@ void lwp_free(struct rt_lwp* lwp)
child = lwp->first_child;
lwp->first_child = child->sibling;
- if (child->finish)
+ if (child->terminated)
{
- lwp_pid_put(lwp_to_pid(child));
- rt_hw_interrupt_enable(level);
+ lwp_pid_put(child);
rt_free(child);
- level = rt_hw_interrupt_disable();
}
else
{
+ /** Note: safe since the slist node is release */
child->sibling = RT_NULL;
+ /* Note: this may cause an orphan lwp */
child->parent = RT_NULL;
}
}
- rt_hw_interrupt_enable(level);
if (!lwp->background)
{
struct termios *old_stdin_termios = get_old_termios();
@@ -462,7 +483,6 @@ void lwp_free(struct rt_lwp* lwp)
{
tcsetattr(1, 0, old_stdin_termios);
}
- level = rt_hw_interrupt_disable();
if (lwp->tty != RT_NULL)
{
rt_mutex_take(&lwp->tty->lock, RT_WAITING_FOREVER);
@@ -480,10 +500,6 @@ void lwp_free(struct rt_lwp* lwp)
lwp->tty = RT_NULL;
}
}
- else
- {
- level = rt_hw_interrupt_disable();
- }
/* for parent */
if (lwp->parent)
@@ -495,7 +511,6 @@ void lwp_free(struct rt_lwp* lwp)
thread->error = RT_EOK;
thread->msg_ret = (void*)(rt_size_t)lwp->lwp_ret;
rt_thread_resume(thread);
- rt_hw_interrupt_enable(level);
return;
}
else
@@ -510,35 +525,28 @@ void lwp_free(struct rt_lwp* lwp)
}
}
- lwp_pid_put(lwp_to_pid(lwp));
- rt_hw_interrupt_enable(level);
+ lwp_pid_put(lwp);
rt_free(lwp);
}
+/** @note the reference is not for synchronization, but for the release of resource. the synchronization is done through lwp & pid lock */
int lwp_ref_inc(struct rt_lwp *lwp)
{
- rt_base_t level;
-
- level = rt_hw_interrupt_disable();
- lwp->ref++;
- rt_hw_interrupt_enable(level);
+ int ref;
+ ref = rt_atomic_add(&lwp->ref, 1);
+ LOG_D("%s(%p(%s)): before %d", __func__, lwp, lwp->cmd, ref);
- return 0;
+ return ref;
}
int lwp_ref_dec(struct rt_lwp *lwp)
{
- rt_base_t level;
- int ref = -1;
+ int ref;
- level = rt_hw_interrupt_disable();
- if (lwp->ref)
- {
- lwp->ref--;
- ref = lwp->ref;
- }
- rt_hw_interrupt_enable(level);
- if (!ref)
+ ref = rt_atomic_add(&lwp->ref, -1);
+ LOG_D("%s(lwp=%p,lwp->cmd=%s): before ref=%d", __func__, lwp, lwp->cmd, ref);
+
+ if (ref == 1)
{
struct rt_channel_msg msg;
@@ -547,7 +555,6 @@ int lwp_ref_dec(struct rt_lwp *lwp)
memset(&msg, 0, sizeof msg);
rt_raw_channel_send(gdb_server_channel(), &msg);
}
- lwp_signal_detach(&lwp->signal);
#ifndef ARCH_MM_MMU
#ifdef RT_LWP_USING_SHM
@@ -555,26 +562,27 @@ int lwp_ref_dec(struct rt_lwp *lwp)
#endif /* RT_LWP_USING_SHM */
#endif /* not defined ARCH_MM_MMU */
lwp_free(lwp);
-
- return 0;
+ }
+ else
+ {
+ /* reference must be a positive integer */
+ RT_ASSERT(ref > 1);
}
- return -1;
+ return ref;
}
-struct rt_lwp* lwp_from_pid(pid_t pid)
+struct rt_lwp* lwp_from_pid_locked(pid_t pid)
{
- rt_base_t level;
struct lwp_avl_struct *p;
struct rt_lwp *lwp = RT_NULL;
- level = rt_hw_interrupt_disable();
p = lwp_avl_find(pid, lwp_pid_root);
if (p)
{
lwp = (struct rt_lwp *)p->data;
}
- rt_hw_interrupt_enable(level);
+
return lwp;
}
@@ -592,12 +600,15 @@ char* lwp_pid2name(int32_t pid)
struct rt_lwp *lwp;
char* process_name = RT_NULL;
- lwp = lwp_from_pid(pid);
+ lwp_pid_lock_take();
+ lwp = lwp_from_pid_locked(pid);
if (lwp)
{
process_name = strrchr(lwp->cmd, '/');
process_name = process_name? process_name + 1: lwp->cmd;
}
+ lwp_pid_lock_release();
+
return process_name;
}
@@ -607,9 +618,8 @@ pid_t lwp_name2pid(const char *name)
pid_t pid = 0;
rt_thread_t main_thread;
char* process_name = RT_NULL;
- rt_base_t level;
- level = rt_hw_interrupt_disable();
+ lwp_pid_lock_take();
for (idx = 0; idx < RT_LWP_MAX_NR; idx++)
{
/* 0 is reserved */
@@ -629,7 +639,7 @@ pid_t lwp_name2pid(const char *name)
}
}
}
- rt_hw_interrupt_enable(level);
+ lwp_pid_lock_release();
return pid;
}
@@ -638,83 +648,141 @@ int lwp_getpid(void)
return ((struct rt_lwp *)rt_thread_self()->lwp)->pid;
}
-pid_t waitpid(pid_t pid, int *status, int options)
+/**
+ * @brief Wait for a child lwp to terminate. Do the essential recycling. Setup
+ * status code for user
+ */
+static sysret_t _lwp_wait_and_recycle(struct rt_lwp *child, rt_thread_t cur_thr,
+ struct rt_lwp *self_lwp, int *status,
+ int options)
{
- pid_t ret = -1;
- rt_base_t level;
- struct rt_thread *thread;
- struct rt_lwp *lwp;
- struct rt_lwp *this_lwp;
-
- this_lwp = lwp_self();
- if (!this_lwp)
- {
- goto quit;
- }
+ sysret_t error;
+ int lwp_stat;
+ int terminated;
- level = rt_hw_interrupt_disable();
- if (pid == -1)
+ if (!child)
{
- lwp = this_lwp->first_child;
- if (!lwp)
- goto quit;
- else
- pid = lwp->pid;
+ error = -RT_ERROR;
}
else
{
- lwp = lwp_from_pid(pid);
- if (!lwp)
+ /**
+ * Note: Critical Section
+ * - child lwp (RW. This will modify its parent if valid)
+ */
+ LWP_LOCK(child);
+ if (child->terminated)
{
- goto quit;
+ error = child->pid;
}
- }
+ else if (rt_list_isempty(&child->wait_list))
+ {
+ /**
+ * Note: only one thread can wait on wait_list.
+ * dont reschedule before mutex unlock
+ */
+ rt_enter_critical();
- if (lwp->parent != this_lwp)
- {
- goto quit;
+ error = rt_thread_suspend_with_flag(cur_thr, RT_INTERRUPTIBLE);
+ if (error == 0)
+ {
+ rt_list_insert_before(&child->wait_list, &(cur_thr->tlist));
+ LWP_UNLOCK(child);
+
+ rt_exit_critical();
+ rt_schedule();
+
+ if (child->terminated)
+ error = child->pid;
+ else
+ error = -RT_EINTR;
+ }
+ else
+ rt_exit_critical();
+ }
+ else
+ error = -RT_EINTR;
+
+ lwp_stat = child->lwp_ret;
+ terminated = child->terminated;
+ if (!terminated)
+ LWP_UNLOCK(child);
+
+ if (error > 0)
+ {
+ if (terminated)
+ {
+ /** Reap the child process if it's exited */
+ lwp_children_unregister(self_lwp, child);
+ child->parent = RT_NULL;
+ lwp_pid_put(child);
+ }
+ if (status)
+ lwp_data_put(self_lwp, status, &lwp_stat, sizeof(*status));
+ }
}
- if (lwp->finish)
+ return error;
+}
+
+pid_t waitpid(pid_t pid, int *status, int options) __attribute__((alias("lwp_waitpid")));
+
+pid_t lwp_waitpid(const pid_t pid, int *status, int options)
+{
+ pid_t rc = -1;
+ struct rt_thread *thread;
+ struct rt_lwp *child;
+ struct rt_lwp *self_lwp;
+
+ thread = rt_thread_self();
+ self_lwp = lwp_self();
+
+ if (!self_lwp)
{
- ret = pid;
+ rc = -RT_EINVAL;
}
else
{
- if (!rt_list_isempty(&lwp->wait_list))
+ if (pid > 0)
{
- goto quit;
+ lwp_pid_lock_take();
+ child = lwp_from_pid_locked(pid);
+ if (child->parent != self_lwp)
+ rc = -RT_ERROR;
+ else
+ rc = RT_EOK;
+ lwp_pid_lock_release();
+
+ if (rc == RT_EOK)
+ rc = _lwp_wait_and_recycle(child, thread, self_lwp, status, options);
+ }
+ else if (pid == -1)
+ {
+ LWP_LOCK(self_lwp);
+ child = self_lwp->first_child;
+ LWP_UNLOCK(self_lwp);
+ RT_ASSERT(!child || child->parent == self_lwp);
+
+ rc = _lwp_wait_and_recycle(child, thread, self_lwp, status, options);
}
- thread = rt_thread_self();
- rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE);
- rt_list_insert_before(&lwp->wait_list, &(thread->tlist));
- rt_schedule();
- if (thread->error == RT_EOK)
+ else
{
- ret = pid;
+ /* not supported yet */
+ rc = -RT_EINVAL;
}
}
- if (ret != -1)
+ if (rc > 0)
{
- /* delete from sibling list of its parent */
- struct rt_lwp **lwp_node;
-
- lwp_data_put(this_lwp, status, &lwp->lwp_ret, sizeof(*status));
- lwp_node = &this_lwp->first_child;
- while (*lwp_node != lwp)
- {
- RT_ASSERT(*lwp_node != RT_NULL);
- lwp_node = &(*lwp_node)->sibling;
- }
- (*lwp_node) = lwp->sibling;
- lwp->parent = RT_NULL;
- lwp_pid_put(pid);
+ LOG_D("%s: recycle child id %ld (status=0x%x)", __func__, (long)rc, status ? *status : 0);
+ }
+ else
+ {
+ RT_ASSERT(rc != 0);
+ LOG_D("%s: wait failed with code %ld", __func__, (long)rc);
}
-quit:
- rt_hw_interrupt_enable(level);
- return ret;
+ return rc;
}
#ifdef RT_USING_FINSH
@@ -811,8 +879,10 @@ long list_process(void)
struct rt_thread th;
thread = threads[index];
+
+ /** FIXME: take the rt_thread_t lock */
level = rt_hw_interrupt_disable();
- if ((thread->parent.type & ~RT_Object_Class_Static) != RT_Object_Class_Thread)
+ if ((rt_object_get_type(&thread->parent) & ~RT_Object_Class_Static) != RT_Object_Class_Thread)
{
rt_hw_interrupt_enable(level);
continue;
@@ -870,7 +940,9 @@ static void cmd_kill(int argc, char** argv)
sig = atoi(argv[3]);
}
}
- lwp_signal_kill(lwp_from_pid(pid), sig, SI_USER, 0);
+ lwp_pid_lock_take();
+ lwp_signal_kill(lwp_from_pid_locked(pid), sig, SI_USER, 0);
+ lwp_pid_lock_release();
}
MSH_CMD_EXPORT_ALIAS(cmd_kill, kill, send a signal to a process);
@@ -885,7 +957,9 @@ static void cmd_killall(int argc, char** argv)
while((pid = lwp_name2pid(argv[1])) > 0)
{
- lwp_signal_kill(lwp_from_pid(pid), SIGKILL, SI_USER, 0);
+ lwp_pid_lock_take();
+ lwp_signal_kill(lwp_from_pid_locked(pid), SIGKILL, SI_USER, 0);
+ lwp_pid_lock_release();
rt_thread_mdelay(100);
}
}
@@ -915,6 +989,7 @@ static int found_thread(struct rt_lwp* lwp, rt_thread_t thread)
rt_base_t level;
rt_list_t *list;
+ /** FIXME: take the rt_thread_t lock */
level = rt_hw_interrupt_disable();
list = lwp->t_grp.next;
while (list != &lwp->t_grp)
@@ -947,6 +1022,7 @@ void lwp_request_thread_exit(rt_thread_t thread_to_exit)
return;
}
+ /* FIXME: take the rt_thread_t lock */
level = rt_hw_interrupt_disable();
main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling);
@@ -993,7 +1069,6 @@ finish:
void lwp_terminate(struct rt_lwp *lwp)
{
- rt_base_t level;
rt_list_t *list;
if (!lwp)
@@ -1004,11 +1079,11 @@ void lwp_terminate(struct rt_lwp *lwp)
LOG_D("%s(lwp=%p \"%s\")", __func__, lwp, lwp->cmd);
- level = rt_hw_interrupt_disable();
+ LWP_LOCK(lwp);
- /* stop the receiving of signals */
if (!lwp->terminated)
{
+ /* stop the receiving of signals */
lwp->terminated = RT_TRUE;
/* broadcast exit request for sibling threads */
@@ -1029,12 +1104,11 @@ void lwp_terminate(struct rt_lwp *lwp)
}
}
}
- rt_hw_interrupt_enable(level);
+ LWP_UNLOCK(lwp);
}
void lwp_wait_subthread_exit(void)
{
- rt_base_t level;
struct rt_lwp *lwp;
rt_thread_t thread;
rt_thread_t main_thread;
@@ -1057,7 +1131,13 @@ void lwp_wait_subthread_exit(void)
int subthread_is_terminated;
LOG_D("%s: wait for subthread exiting", __func__);
- level = rt_hw_interrupt_disable();
+ /**
+ * Brief: wait for all *running* sibling threads to exit
+ *
+ * Note: Critical Section
+ * - sibling list of lwp (RW. It will clear all siblings finally)
+ */
+ LWP_LOCK(lwp);
subthread_is_terminated = (int)(thread->sibling.prev == &lwp->t_grp);
if (!subthread_is_terminated)
{
@@ -1083,12 +1163,21 @@ void lwp_wait_subthread_exit(void)
{
sub_thread = rt_list_entry(list, struct rt_thread, sibling);
rt_list_remove(&sub_thread->sibling);
+
+ /**
+ * Note: Critical Section
+ * - thread control block (RW. Since it will free the thread
+ * control block, it must ensure no one else can access
+ * thread any more)
+ */
+ lwp_tid_put(sub_thread->tid);
+ sub_thread->tid = 0;
rt_thread_delete(sub_thread);
}
subthread_is_terminated = 1;
}
}
- rt_hw_interrupt_enable(level);
+ LWP_UNLOCK(lwp);
if (subthread_is_terminated)
{
@@ -1103,7 +1192,8 @@ static int _lwp_setaffinity(pid_t pid, int cpu)
struct rt_lwp *lwp;
int ret = -1;
- lwp = lwp_from_pid(pid);
+ lwp_pid_lock_take();
+ lwp = lwp_from_pid_locked(pid);
if (lwp)
{
#ifdef RT_USING_SMP
@@ -1120,12 +1210,12 @@ static int _lwp_setaffinity(pid_t pid, int cpu)
#endif
ret = 0;
}
+ lwp_pid_lock_release();
return ret;
}
int lwp_setaffinity(pid_t pid, int cpu)
{
- rt_base_t level;
int ret;
#ifdef RT_USING_SMP
@@ -1134,9 +1224,7 @@ int lwp_setaffinity(pid_t pid, int cpu)
cpu = RT_CPUS_NR;
}
#endif
- level = rt_hw_interrupt_disable();
ret = _lwp_setaffinity(pid, cpu);
- rt_hw_interrupt_enable(level);
return ret;
}