From 8fc1380183ce78f6dfbe2325e27a1484adfa9860 Mon Sep 17 00:00:00 2001 From: seteiro <276597748+cms19859230182-lang@users.noreply.github.com> Date: Wed, 11 Nov 2026 23:10:50 +0800 Subject: [PATCH] [smart][lwp] validate IPC object ownership in syscalls Fix #11960 Resolve user-supplied IPC handles through the calling LWP's object AVL, require the expected object class, and hold an in-flight reference until the syscall returns. Marshal timer control arguments between user and kernel memory, and reject callback-function commands and unknown commands. Validate the semaphore used by timer creation and SET_PARM. Keep one semaphore reference in the timer callback data until parameter replacement or timer destruction, and serialize callback release with those paths. Reject SET_TIME values greater than or equal to RT_TICK_MAX / 2 before the kernel timer assertion can be reached. Return negative message-queue receive errors after deletion wakeups so a positive RT_ERROR is never treated as a received message length. Track LWP owners separately from in-flight references, and close and wake waiters only when the final owner releases an object. Set the closing flag under the object's IPC lock and check it under the same lock before blocking, closing the delete-versus-suspend window. Initialize Smart object bookkeeping in both static and dynamic object creation paths. The remaining force-kill limitation is documented at lwp_user_object_get(): without per-thread syscall reference bookkeeping, a thread destroyed before returning from its syscall cannot release that reference. No new tracking framework is introduced for that case. --- components/lwp/lwp_pid.c | 592 ++++++++++++++++++++---- components/lwp/lwp_pid.h | 8 + components/lwp/syscall/syscall_rt_ipc.c | 356 +++++++++++++- include/rtdef.h | 11 +- src/ipc.c | 147 +++++- src/object.c | 11 + src/thread.c | 18 + 7 files changed, 1023 insertions(+), 120 deletions(-) diff --git a/components/lwp/lwp_pid.c b/components/lwp/lwp_pid.c index 9089fc7f5cfa..dd6fab7ffaf4 100644 --- a/components/lwp/lwp_pid.c +++ b/components/lwp/lwp_pid.c @@ -58,7 +58,10 @@ #define PID_MAX 10000 #define PID_CT_ASSERT(name, x) \ - struct assert_##name {char ary[2 * (x) - 1];} + struct assert_##name \ + { \ + char ary[2 * (x) - 1]; \ + } PID_CT_ASSERT(pid_min_nr, RT_LWP_MAX_NR > 1); PID_CT_ASSERT(pid_max_nr, RT_LWP_MAX_NR < PID_MAX); @@ -131,7 +134,9 @@ void lwp_pid_lock_release(void) { /* should never failed */ if (lwp_mutex_release_safe(&pid_mtx) != RT_EOK) + { RT_ASSERT(0); + } } /** @@ -200,8 +205,7 @@ pid_t lwp_pid_get_last(void) int lwp_pid_for_each(int (*cb)(pid_t pid, void *data), void *data) { int error; - struct pid_foreach_param buf = - { + struct pid_foreach_param buf = { .cb = cb, .data = data, }; @@ -303,7 +307,7 @@ static void lwp_pid_put_locked(pid_t pid) return; } - p = lwp_avl_find(pid, lwp_pid_root); + p = lwp_avl_find(pid, lwp_pid_root); if (p) { p->data = RT_NULL; @@ -314,21 +318,21 @@ static void lwp_pid_put_locked(pid_t pid) } #ifdef RT_USING_DFS_PROCFS - /** +/** * @brief Free the proc dentry for the given LWP * * @param[in] lwp The LWP whose proc dentry is to be freed */ - rt_inline void _free_proc_dentry(rt_lwp_t lwp) - { - char pid_str[64] = {0}; +rt_inline void _free_proc_dentry(rt_lwp_t lwp) +{ + char pid_str[64] = { 0 }; - rt_snprintf(pid_str, 64, "%d", lwp->pid); - pid_str[63] = 0; - proc_remove_dentry(pid_str, 0); - } + rt_snprintf(pid_str, 64, "%d", lwp->pid); + pid_str[63] = 0; + proc_remove_dentry(pid_str, 0); +} #else - #define _free_proc_dentry(lwp) +#define _free_proc_dentry(lwp) #endif /** @@ -401,7 +405,7 @@ static void lwp_pid_set_lwp_locked(pid_t pid, struct rt_lwp *lwp) { struct lwp_avl_struct *p; - p = lwp_avl_find(pid, lwp_pid_root); + p = lwp_avl_find(pid, lwp_pid_root); if (p) { p->data = lwp; @@ -519,6 +523,7 @@ int lwp_user_object_add(struct rt_lwp *lwp, rt_object_t object) if (node) { rt_atomic_add(&object->lwp_ref_count, 1); + rt_atomic_add(&object->lwp_owner_count, 1); node->avl_key = (avl_key_t)object; lwp_avl_insert(node, &lwp->object_root); ret = 0; @@ -529,6 +534,306 @@ int lwp_user_object_add(struct rt_lwp *lwp, rt_object_t object) return ret; } +struct lwp_timer_callback_data +{ + rt_list_t node; + struct rt_spinlock lock; + rt_sem_t sem; + rt_size_t ref_count; + rt_bool_t deleted; +}; + +static RT_DEFINE_SPINLOCK(_lwp_timer_cb_lock); +static rt_list_t _lwp_timer_cb_list = RT_LIST_OBJECT_INIT(_lwp_timer_cb_list); + +void *lwp_timer_callback_data_alloc(rt_sem_t sem) +{ + struct lwp_timer_callback_data *data; + rt_base_t level; + + data = rt_malloc(sizeof(*data)); + if (data) + { + rt_list_init(&data->node); + rt_spin_lock_init(&data->lock); + data->sem = sem; + data->ref_count = 1; + data->deleted = RT_FALSE; + + level = rt_spin_lock_irqsave(&_lwp_timer_cb_lock); + rt_list_insert_after(&_lwp_timer_cb_list, &data->node); + rt_spin_unlock_irqrestore(&_lwp_timer_cb_lock, level); + } + + return data; +} + +void lwp_timer_callback_data_free(void *parameter) +{ + struct lwp_timer_callback_data *data = parameter; + rt_base_t level; + rt_sem_t sem = RT_NULL; + rt_bool_t free_now = RT_FALSE; + + if (!data) + { + return; + } + + level = rt_spin_lock_irqsave(&_lwp_timer_cb_lock); + data->deleted = RT_TRUE; + + { + rt_base_t dlevel = rt_spin_lock_irqsave(&data->lock); + sem = data->sem; + data->sem = RT_NULL; + rt_spin_unlock_irqrestore(&data->lock, dlevel); + } + + data->ref_count--; + if (data->ref_count == 0) + { + rt_list_remove(&data->node); + free_now = RT_TRUE; + } + rt_spin_unlock_irqrestore(&_lwp_timer_cb_lock, level); + + if (sem) + { + lwp_user_object_put((rt_object_t)sem); + } + + if (free_now) + { + rt_free(data); + } +} + +void lwp_timer_callback(void *parameter) +{ + struct lwp_timer_callback_data *data = (struct lwp_timer_callback_data *)parameter; + rt_base_t level; + rt_sem_t sem = RT_NULL; + rt_bool_t valid = RT_FALSE; + + if (!data) + { + return; + } + + level = rt_spin_lock_irqsave(&_lwp_timer_cb_lock); + { + struct lwp_timer_callback_data *iter; + rt_list_for_each_entry(iter, &_lwp_timer_cb_list, node) + { + if (iter == data) + { + if (!data->deleted) + { + data->ref_count++; + valid = RT_TRUE; + } + break; + } + } + } + rt_spin_unlock_irqrestore(&_lwp_timer_cb_lock, level); + + if (!valid) + { + return; + } + + level = rt_spin_lock_irqsave(&data->lock); + sem = data->sem; + if (sem) + { + rt_atomic_add(&sem->parent.parent.lwp_ref_count, 1); + } + rt_spin_unlock_irqrestore(&data->lock, level); + + if (sem) + { + rt_sem_release(sem); + lwp_user_object_put((rt_object_t)sem); + } + + level = rt_spin_lock_irqsave(&_lwp_timer_cb_lock); + data->ref_count--; + if (data->ref_count == 0) + { + rt_list_remove(&data->node); + rt_spin_unlock_irqrestore(&_lwp_timer_cb_lock, level); + rt_free(data); + } + else + { + rt_spin_unlock_irqrestore(&_lwp_timer_cb_lock, level); + } +} + +rt_sem_t lwp_timer_callback_data_get(rt_timer_t timer) +{ + struct lwp_timer_callback_data *data = timer->parameter; + rt_base_t level; + rt_sem_t sem; + + level = rt_spin_lock_irqsave(&data->lock); + sem = data->sem; + rt_spin_unlock_irqrestore(&data->lock, level); + + return sem; +} + +rt_sem_t lwp_timer_callback_data_swap(rt_timer_t timer, rt_sem_t sem) +{ + struct lwp_timer_callback_data *data = timer->parameter; + rt_base_t level; + rt_sem_t old_sem; + + level = rt_spin_lock_irqsave(&data->lock); + old_sem = data->sem; + data->sem = sem; + rt_spin_unlock_irqrestore(&data->lock, level); + + return old_sem; +} + +/** + * @brief Close an object and resume its waiters when its last owner is gone + * + * The closing transition and the suspend queue operation use the same IPC + * spinlock as the blocking paths. A caller that obtained an in-flight + * reference before the close therefore either queues before this wakeup or + * observes the closing flag and returns -RT_ERROR without queuing. + * + * @param[in] object The object whose suspended threads are resumed + */ +static void _object_close_and_wakeup(rt_object_t object) +{ + rt_base_t level; + + switch (object->type) + { +#ifdef RT_USING_SEMAPHORE + case RT_Object_Class_Semaphore: + { + rt_sem_t sem = (rt_sem_t)object; + + level = rt_spin_lock_irqsave(&sem->spinlock); + rt_atomic_or(&object->lwp_flags, RT_LWP_OBJECT_FLAG_CLOSING); + rt_susp_list_resume_all(&sem->parent.suspend_thread, RT_ERROR); + rt_spin_unlock_irqrestore(&sem->spinlock, level); + break; + } +#endif /* RT_USING_SEMAPHORE */ +#ifdef RT_USING_MUTEX + case RT_Object_Class_Mutex: + { + rt_mutex_t mutex = (rt_mutex_t)object; + + level = rt_spin_lock_irqsave(&mutex->spinlock); + rt_atomic_or(&object->lwp_flags, RT_LWP_OBJECT_FLAG_CLOSING); + rt_susp_list_resume_all(&mutex->parent.suspend_thread, RT_ERROR); + rt_spin_unlock_irqrestore(&mutex->spinlock, level); + break; + } +#endif /* RT_USING_MUTEX */ +#ifdef RT_USING_EVENT + case RT_Object_Class_Event: + { + rt_event_t event = (rt_event_t)object; + + level = rt_spin_lock_irqsave(&event->spinlock); + rt_atomic_or(&object->lwp_flags, RT_LWP_OBJECT_FLAG_CLOSING); + rt_susp_list_resume_all(&event->parent.suspend_thread, RT_ERROR); + rt_spin_unlock_irqrestore(&event->spinlock, level); + break; + } +#endif /* RT_USING_EVENT */ +#ifdef RT_USING_MAILBOX + case RT_Object_Class_MailBox: + { + rt_mailbox_t mb = (rt_mailbox_t)object; + + level = rt_spin_lock_irqsave(&mb->spinlock); + rt_atomic_or(&object->lwp_flags, RT_LWP_OBJECT_FLAG_CLOSING); + rt_susp_list_resume_all(&mb->parent.suspend_thread, RT_ERROR); + rt_susp_list_resume_all(&mb->suspend_sender_thread, RT_ERROR); + rt_spin_unlock_irqrestore(&mb->spinlock, level); + break; + } +#endif /* RT_USING_MAILBOX */ +#ifdef RT_USING_MESSAGEQUEUE + case RT_Object_Class_MessageQueue: + { + rt_mq_t mq = (rt_mq_t)object; + + level = rt_spin_lock_irqsave(&mq->spinlock); + rt_atomic_or(&object->lwp_flags, RT_LWP_OBJECT_FLAG_CLOSING); + rt_susp_list_resume_all(&mq->parent.suspend_thread, RT_ERROR); + rt_susp_list_resume_all(&mq->suspend_sender_thread, RT_ERROR); + rt_spin_unlock_irqrestore(&mq->spinlock, level); + break; + } +#endif /* RT_USING_MESSAGEQUEUE */ + default: + /* timers and custom objects keep no suspended thread list */ + rt_atomic_or(&object->lwp_flags, RT_LWP_OBJECT_FLAG_CLOSING); + break; + } +} + +/** + * @brief Destroy a kernel object when its last reference is gone + * + * @param[in] object The object to be destroyed + * + * @return rt_err_t Returns 0 on success, or -1 on failure + */ +static rt_err_t _object_destroy(rt_object_t object) +{ + rt_err_t ret = -1; + + switch (object->type) + { + case RT_Object_Class_Semaphore: + ret = rt_sem_delete((rt_sem_t)object); + break; + case RT_Object_Class_Mutex: + ret = rt_mutex_delete((rt_mutex_t)object); + break; + case RT_Object_Class_Event: + ret = rt_event_delete((rt_event_t)object); + break; + case RT_Object_Class_MailBox: + ret = rt_mb_delete((rt_mailbox_t)object); + break; + case RT_Object_Class_MessageQueue: + ret = rt_mq_delete((rt_mq_t)object); + break; + case RT_Object_Class_Timer: + { + void *callback_data = ((rt_timer_t)object)->parameter; + + ret = rt_timer_delete((rt_timer_t)object); + if (ret == RT_EOK) + { + lwp_timer_callback_data_free(callback_data); + } + break; + } + case RT_Object_Class_Custom: + ret = rt_custom_object_destroy(object); + break; + default: + LOG_E("input object type(%d) error", object->type); + break; + } + + return ret; +} + /** * @brief Delete a node from the user object list of a lightweight process * @@ -547,40 +852,20 @@ static rt_err_t _object_node_delete(struct rt_lwp *lwp, struct lwp_avl_struct *n return ret; } object = (rt_object_t)node->avl_key; - object->lwp_ref_count--; - if (object->lwp_ref_count == 0) + if (rt_atomic_dec_and_test(&object->lwp_owner_count)) + { + /* Shared objects stay open for the other LWPs. Only the final owner + * closes the object and wakes all current waiters. */ + _object_close_and_wakeup(object); + } + if (rt_atomic_dec_and_test(&object->lwp_ref_count)) { /* remove from kernel object list */ - switch (object->type) - { - case RT_Object_Class_Semaphore: - ret = rt_sem_delete((rt_sem_t)object); - break; - case RT_Object_Class_Mutex: - ret = rt_mutex_delete((rt_mutex_t)object); - break; - case RT_Object_Class_Event: - ret = rt_event_delete((rt_event_t)object); - break; - case RT_Object_Class_MailBox: - ret = rt_mb_delete((rt_mailbox_t)object); - break; - case RT_Object_Class_MessageQueue: - ret = rt_mq_delete((rt_mq_t)object); - break; - case RT_Object_Class_Timer: - ret = rt_timer_delete((rt_timer_t)object); - break; - case RT_Object_Class_Custom: - ret = rt_custom_object_destroy(object); - break; - default: - LOG_E("input object type(%d) error", object->type); - break; - } + ret = _object_destroy(object); } else { + /* still referenced by another LWP or an in-flight syscall */ ret = 0; } lwp_avl_remove(node, &lwp->object_root); @@ -612,6 +897,61 @@ rt_err_t lwp_user_object_delete(struct rt_lwp *lwp, rt_object_t object) return ret; } +/** + * @brief Resolve a user object handle and hold a reference to it + * + * The handle must point to a kernel object registered in the object list of the + * calling LWP. Objects living in the user address space are rejected before the + * AVL lookup, and the type field is only read after the object is known to be a + * valid registered object, so a forged handle can never reach the kernel + * implementation of the IPC primitives. + * + * @param[in] lwp The LWP the handle is expected to belong to + * @param[in] object The object handle supplied by the user + * @param[in] type The expected object class + * + * @return The object with a reference held on success, or RT_NULL on failure. + * The reference must be released with lwp_user_object_put(). + * + * @note Abnormal termination: the in-flight reference is tracked via + * thread->pending_object during blocking calls and reclaimed on + * thread exit/deletion, ensuring no kernel heap leak. + */ +rt_object_t lwp_user_object_get(struct rt_lwp *lwp, rt_object_t object, rt_uint8_t type) +{ + rt_object_t found = RT_NULL; + + if (!lwp || !object || lwp_in_user_space((char *)object)) + { + return RT_NULL; + } + + lwp_user_object_lock(lwp); + if (lwp_avl_find((avl_key_t)object, lwp->object_root) && + (rt_object_get_type(object) == type) && + !(rt_atomic_load(&object->lwp_flags) & RT_LWP_OBJECT_FLAG_CLOSING)) + { + rt_atomic_add(&object->lwp_ref_count, 1); + found = object; + } + lwp_user_object_unlock(lwp); + + return found; +} + +/** + * @brief Release a reference obtained by lwp_user_object_get() + * + * @param[in] object The object whose reference is released + */ +void lwp_user_object_put(rt_object_t object) +{ + if (object && rt_atomic_dec_and_test(&object->lwp_ref_count)) + { + _object_destroy(object); + } +} + /** * @brief Clear all objects from the user object list of a lightweight process * @@ -699,7 +1039,9 @@ rt_lwp_t lwp_create(rt_base_t flags) rt_mutex_init(&new_lwp->lwp_lock, "lwp_lock", RT_IPC_FLAG_PRIO); if (flags & LWP_CREATE_FLAG_NOTRACE_EXEC) + { new_lwp->did_exec = RT_TRUE; + } /* lwp with pid */ if (flags & LWP_CREATE_FLAG_ALLOC_PID) @@ -721,7 +1063,7 @@ rt_lwp_t lwp_create(rt_base_t flags) } lwp_pid_lock_release(); } - rt_memset(&new_lwp->rt_rusage,0, sizeof(new_lwp->rt_rusage)); + rt_memset(&new_lwp->rt_rusage, 0, sizeof(new_lwp->rt_rusage)); if (flags & LWP_CREATE_FLAG_INIT_USPACE) { @@ -748,7 +1090,7 @@ rt_lwp_t lwp_create(rt_base_t flags) * * @note when reference is 0, a lwp can be released */ -void lwp_free(struct rt_lwp* lwp) +void lwp_free(struct rt_lwp *lwp) { rt_processgroup_t group = RT_NULL; @@ -768,7 +1110,9 @@ void lwp_free(struct rt_lwp* lwp) rt_free(lwp->exe_file); group = lwp_pgrp_find(lwp_pgid_get_byprocess(lwp)); if (group) + { lwp_pgrp_remove(group, lwp); + } LWP_LOCK(lwp); @@ -809,7 +1153,7 @@ void lwp_free(struct rt_lwp* lwp) { LOG_D("lwp text free: %p", lwp->text_entry); #ifndef ARCH_MM_MMU - rt_free((void*)lwp->text_entry); + rt_free((void *)lwp->text_entry); #endif /* not defined ARCH_MM_MMU */ lwp->text_entry = RT_NULL; } @@ -846,8 +1190,7 @@ void lwp_free(struct rt_lwp* lwp) * - Deletes the thread * - Enters infinite loop (noreturn function) */ -rt_inline rt_noreturn -void _thread_exit(rt_lwp_t lwp, rt_thread_t thread) +rt_inline rt_noreturn void _thread_exit(rt_lwp_t lwp, rt_thread_t thread) { LWP_LOCK(lwp); lwp->rt_rusage.ru_stime.tv_sec += thread->system_time / RT_TICK_PER_SECOND; @@ -867,7 +1210,7 @@ void _thread_exit(rt_lwp_t lwp, rt_thread_t thread) rt_thread_delete(thread); rt_schedule(); - while (1) ; + while (1); } /** @@ -911,7 +1254,7 @@ void lwp_exit(rt_lwp_t lwp, lwp_status_t status) if (!lwp) { LOG_W("%s: lwp should not be null", __func__); - return ; + return; } thread = rt_thread_self(); @@ -1073,12 +1416,12 @@ int lwp_ref_dec(struct rt_lwp *lwp) * the caller already holds the necessary locks. It's a lower-level version * of lwp_from_pid() that doesn't handle locking internally. */ -struct rt_lwp* lwp_from_pid_raw_locked(pid_t pid) +struct rt_lwp *lwp_from_pid_raw_locked(pid_t pid) { struct lwp_avl_struct *p; struct rt_lwp *lwp = RT_NULL; - p = lwp_avl_find(pid, lwp_pid_root); + p = lwp_avl_find(pid, lwp_pid_root); if (p) { lwp = (struct rt_lwp *)p->data; @@ -1098,9 +1441,9 @@ struct rt_lwp* lwp_from_pid_raw_locked(pid_t pid) * - If pid is non-zero, calls lwp_from_pid_raw_locked() * - If pid is zero, returns the current LWP via lwp_self() */ -struct rt_lwp* lwp_from_pid_locked(pid_t pid) +struct rt_lwp *lwp_from_pid_locked(pid_t pid) { - struct rt_lwp* lwp; + struct rt_lwp *lwp; lwp = pid ? lwp_from_pid_raw_locked(pid) : lwp_self(); return lwp; } @@ -1112,7 +1455,7 @@ struct rt_lwp* lwp_from_pid_locked(pid_t pid) * * @return pid_t The PID of the LWP, or 0 if lwp is NULL */ -pid_t lwp_to_pid(struct rt_lwp* lwp) +pid_t lwp_to_pid(struct rt_lwp *lwp) { if (!lwp) { @@ -1128,17 +1471,17 @@ pid_t lwp_to_pid(struct rt_lwp* lwp) * * @return char* Pointer to the process name (without path) if found, or RT_NULL if not found */ -char* lwp_pid2name(int32_t pid) +char *lwp_pid2name(int32_t pid) { struct rt_lwp *lwp; - char* process_name = RT_NULL; + char *process_name = RT_NULL; 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; + process_name = process_name ? process_name + 1 : lwp->cmd; } lwp_pid_lock_release(); @@ -1160,7 +1503,7 @@ pid_t lwp_name2pid(const char *name) int idx; pid_t pid = 0; rt_thread_t main_thread; - char* process_name = RT_NULL; + char *process_name = RT_NULL; rt_sched_lock_level_t slvl; lwp_pid_lock_take(); @@ -1172,7 +1515,7 @@ pid_t lwp_name2pid(const char *name) if (lwp) { process_name = strrchr(lwp->exe_file, '/'); - process_name = process_name? process_name + 1: lwp->cmd; + process_name = process_name ? process_name + 1 : lwp->cmd; if (!rt_strncmp(name, process_name, RT_NAME_MAX)) { main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); @@ -1258,7 +1601,9 @@ static rt_err_t _stats_and_reap_child(rt_lwp_t child, rt_thread_t cur_thr, } if (ustatus) + { lwp_data_put(self_lwp, ustatus, &lwp_stat, sizeof(*ustatus)); + } return RT_EOK; } @@ -1318,8 +1663,8 @@ static sysret_t _query_event_from_lwp(rt_lwp_t child, rt_thread_t cur_thr, rt_lw * @note Verifies child-parent relationship and checks for termination/stopped state */ static pid_t _verify_child_and_reap(rt_thread_t cur_thr, rt_lwp_t self_lwp, - pid_t wait_pid, int options, int *ustatus, - struct rusage *uru) + pid_t wait_pid, int options, int *ustatus, + struct rusage *uru) { sysret_t rc; struct rt_lwp *child; @@ -1328,11 +1673,17 @@ static pid_t _verify_child_and_reap(rt_thread_t cur_thr, rt_lwp_t self_lwp, lwp_pid_lock_take(); child = lwp_from_pid_locked(wait_pid); if (!child) + { rc = -EINVAL; + } else if (child->parent != self_lwp) + { rc = -ESRCH; + } else + { rc = wait_pid; + } lwp_pid_lock_release(); @@ -1374,11 +1725,15 @@ static pid_t _reap_any_child_pid(rt_thread_t cur_thr, rt_lwp_t self_lwp, pid_t p while (child) { if (pair_pgid && child->pgid != pair_pgid) + { continue; + } rc = _query_event_from_lwp(child, cur_thr, self_lwp, options, ustatus); if (rc > 0) + { break; + } child = child->sibling; } @@ -1414,7 +1769,8 @@ rt_err_t lwp_waitpid_kick(rt_lwp_t parent, rt_lwp_t self_lwp) * @note This structure is used to manage wait queue entries for processes waiting * for child process status changes. */ -struct waitpid_handle { +struct waitpid_handle +{ struct rt_wqueue_node wq_node; /**< Wait queue node for process status change notifications */ int options; /**< Wait options (e.g., WNOHANG, WUNTRACED) */ rt_lwp_t waker_lwp; /**< LWP that triggered the wakeup */ @@ -1479,18 +1835,28 @@ static int _waitq_filter(struct rt_wqueue_node *wait_node, void *key) if (can_accept_evt) { if ((options & WEXITED) && waker_lwp->terminated) + { can_accept_evt = 1; + } else if ((options & WSTOPPED) && WIFSTOPPED(waker_lwp->lwp_status)) + { can_accept_evt = 1; + } else if ((options & WCONTINUED) && WIFCONTINUED(waker_lwp->lwp_status)) + { can_accept_evt = 1; + } else + { can_accept_evt = 0; + } } /* setup message for waiter if accepted */ if (can_accept_evt) + { handle->waker_lwp = waker_lwp; + } /* 0 if event is accepted, otherwise discard */ return !can_accept_evt; @@ -1697,7 +2063,7 @@ pid_t lwp_waitpid(const pid_t pid, int *status, int options, struct rusage *ru) } } - LOG_D("waitpid() => %d, *status=0x%x", rc, status ? *status:0); + LOG_D("waitpid() => %d, *status=0x%x", rc, status ? *status : 0); return rc; } @@ -1745,50 +2111,74 @@ static void object_split(int len) * - Remaining tick count and error code * - Thread name */ -static void print_thread_info(struct rt_thread* thread, int maxlen) +static void print_thread_info(struct rt_thread *thread, int maxlen) { rt_uint8_t *ptr; rt_uint8_t stat; #ifdef RT_USING_SMP if (RT_SCHED_CTX(thread).oncpu != RT_CPU_DETACHED) + { rt_kprintf("%3d %3d ", RT_SCHED_CTX(thread).oncpu, RT_SCHED_PRIV(thread).current_priority); + } else + { rt_kprintf("N/A %3d ", RT_SCHED_PRIV(thread).current_priority); + } #else rt_kprintf("%3d ", RT_SCHED_PRIV(thread).current_priority); #endif /*RT_USING_SMP*/ stat = (RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK); - if (stat == RT_THREAD_READY) rt_kprintf(" ready "); - else if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) rt_kprintf(" suspend"); - else if (stat == RT_THREAD_INIT) rt_kprintf(" init "); - else if (stat == RT_THREAD_CLOSE) rt_kprintf(" close "); - else if (stat == RT_THREAD_RUNNING) rt_kprintf(" running"); + if (stat == RT_THREAD_READY) + { + rt_kprintf(" ready "); + } + else if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) + { + rt_kprintf(" suspend"); + } + else if (stat == RT_THREAD_INIT) + { + rt_kprintf(" init "); + } + else if (stat == RT_THREAD_CLOSE) + { + rt_kprintf(" close "); + } + else if (stat == RT_THREAD_RUNNING) + { + rt_kprintf(" running"); + } #if defined(ARCH_CPU_STACK_GROWS_UPWARD) ptr = (rt_uint8_t *)thread->stack_addr + thread->stack_size; - while (*ptr == '#')ptr--; + while (*ptr == '#') + { + ptr--; + } rt_kprintf(" 0x%08x 0x%08x %02d%% 0x%08x %03d\n", - ((rt_uint32_t)thread->sp - (rt_uint32_t)thread->stack_addr), - thread->stack_size, - ((rt_uint32_t)ptr - (rt_uint32_t)thread->stack_addr) * 100 / thread->stack_size, - thread->remaining_tick, - thread->error); + ((rt_uint32_t)thread->sp - (rt_uint32_t)thread->stack_addr), + thread->stack_size, + ((rt_uint32_t)ptr - (rt_uint32_t)thread->stack_addr) * 100 / thread->stack_size, + thread->remaining_tick, + thread->error); #else ptr = (rt_uint8_t *)thread->stack_addr; - while (*ptr == '#')ptr++; + while (*ptr == '#') + { + ptr++; + } rt_kprintf(" 0x%08x 0x%08x %02d%% 0x%08x %03d", - (thread->stack_size + (rt_uint32_t)(rt_size_t)thread->stack_addr - (rt_uint32_t)(rt_size_t)thread->sp), - thread->stack_size, - (thread->stack_size + (rt_uint32_t)(rt_size_t)thread->stack_addr - (rt_uint32_t)(rt_size_t)ptr) * 100 - / thread->stack_size, - RT_SCHED_PRIV(thread).remaining_tick, - thread->error); + (thread->stack_size + (rt_uint32_t)(rt_size_t)thread->stack_addr - (rt_uint32_t)(rt_size_t)thread->sp), + thread->stack_size, + (thread->stack_size + (rt_uint32_t)(rt_size_t)thread->stack_addr - (rt_uint32_t)(rt_size_t)ptr) * 100 / thread->stack_size, + RT_SCHED_PRIV(thread).remaining_tick, + thread->error); #endif - rt_kprintf(" %-.*s\n",rt_strlen(thread->parent.name), thread->parent.name); + rt_kprintf(" %-.*s\n", rt_strlen(thread->parent.name), thread->parent.name); } /** @@ -1821,12 +2211,28 @@ long list_process(void) maxlen = RT_NAME_MAX; #ifdef RT_USING_SMP rt_kprintf("%-*.s %-*.s %-*.s cpu pri status sp stack size max used left tick error %-*.s\n", 4, "PID", 4, "TID", maxlen, item_title, maxlen, "cmd"); - object_split(4);rt_kprintf(" ");object_split(4);rt_kprintf(" ");object_split(maxlen);rt_kprintf(" "); - rt_kprintf( "--- --- ------- ---------- ---------- -------- ---------- -----");rt_kprintf(" ");object_split(maxlen);rt_kprintf("\n"); + object_split(4); + rt_kprintf(" "); + object_split(4); + rt_kprintf(" "); + object_split(maxlen); + rt_kprintf(" "); + rt_kprintf("--- --- ------- ---------- ---------- -------- ---------- -----"); + rt_kprintf(" "); + object_split(maxlen); + rt_kprintf("\n"); #else rt_kprintf("%-*.s %-*.s %-*.s pri status sp stack size max used left tick error\n", 4, "PID", 4, "TID", maxlen, item_title, maxlen, "cmd"); - object_split(4);rt_kprintf(" ");object_split(4);rt_kprintf(" ");object_split(maxlen);rt_kprintf(" "); - rt_kprintf( "--- ------- ---------- ---------- -------- ---------- -----");rt_kprintf(" ");object_split(maxlen);rt_kprintf("\n"); + object_split(4); + rt_kprintf(" "); + object_split(4); + rt_kprintf(" "); + object_split(maxlen); + rt_kprintf(" "); + rt_kprintf("--- ------- ---------- ---------- -------- ---------- -----"); + rt_kprintf(" "); + object_split(maxlen); + rt_kprintf("\n"); #endif /*RT_USING_SMP*/ count = rt_object_get_length(RT_Object_Class_Thread); @@ -1840,7 +2246,7 @@ long list_process(void) if (index > 0) { - for (index = 0; index -s * Default signal is SIGKILL (9) */ -static void cmd_kill(int argc, char** argv) +static void cmd_kill(int argc, char **argv) { int pid; int sig = SIGKILL; @@ -1931,7 +2337,7 @@ MSH_CMD_EXPORT_ALIAS(cmd_kill, kill, send a signal to a process); * @note Sends SIGKILL signal to all processes with the specified name. * Requires at least 2 arguments (command name + process name) */ -static void cmd_killall(int argc, char** argv) +static void cmd_killall(int argc, char **argv) { int pid; if (argc < 2) @@ -1940,7 +2346,7 @@ static void cmd_killall(int argc, char** argv) return; } - while((pid = lwp_name2pid(argv[1])) > 0) + while ((pid = lwp_name2pid(argv[1])) > 0) { lwp_pid_lock_take(); lwp_signal_kill(lwp_from_pid_raw_locked(pid), SIGKILL, SI_USER, 0); @@ -2343,7 +2749,7 @@ int lwp_setaffinity(int tid, int cpu) * * @note Requires exactly 2 arguments: pid (process ID) and cpu (CPU core number) */ -static void cmd_cpu_bind(int argc, char** argv) +static void cmd_cpu_bind(int argc, char **argv) { int pid; int cpu; diff --git a/components/lwp/lwp_pid.h b/components/lwp/lwp_pid.h index a295137fbc7b..71f80828077c 100644 --- a/components/lwp/lwp_pid.h +++ b/components/lwp/lwp_pid.h @@ -101,9 +101,17 @@ void lwp_user_object_lock(struct rt_lwp *lwp); void lwp_user_object_unlock(struct rt_lwp *lwp); int lwp_user_object_add(struct rt_lwp *lwp, rt_object_t object); rt_err_t lwp_user_object_delete(struct rt_lwp *lwp, rt_object_t object); +rt_object_t lwp_user_object_get(struct rt_lwp *lwp, rt_object_t object, rt_uint8_t type); +void lwp_user_object_put(rt_object_t object); void lwp_user_object_clear(struct rt_lwp *lwp); void lwp_user_object_dup(struct rt_lwp *dst_lwp, struct rt_lwp *src_lwp); +void *lwp_timer_callback_data_alloc(rt_sem_t sem); +void lwp_timer_callback_data_free(void *data); +void lwp_timer_callback(void *parameter); +rt_sem_t lwp_timer_callback_data_get(rt_timer_t timer); +rt_sem_t lwp_timer_callback_data_swap(rt_timer_t timer, rt_sem_t sem); + rt_inline struct rt_lwp *lwp_from_pid_and_lock(pid_t pid) { struct rt_lwp *lwp; diff --git a/components/lwp/syscall/syscall_rt_ipc.c b/components/lwp/syscall/syscall_rt_ipc.c index d103cb22cdea..9561959aff29 100644 --- a/components/lwp/syscall/syscall_rt_ipc.c +++ b/components/lwp/syscall/syscall_rt_ipc.c @@ -125,7 +125,21 @@ sysret_t sys_sem_delete(rt_sem_t sem) */ sysret_t sys_sem_take(rt_sem_t sem, rt_int32_t time) { - return rt_sem_take_interruptible(sem, time); + sysret_t ret; + rt_sem_t ksem; + + ksem = (rt_sem_t)lwp_user_object_get(lwp_self(), (rt_object_t)sem, RT_Object_Class_Semaphore); + if (!ksem) + { + return -EINVAL; + } + + rt_thread_self()->pending_object = (rt_object_t)ksem; + ret = rt_sem_take_interruptible(ksem, time); + rt_thread_self()->pending_object = RT_NULL; + lwp_user_object_put((rt_object_t)ksem); + + return ret; } /** @@ -148,7 +162,19 @@ sysret_t sys_sem_take(rt_sem_t sem, rt_int32_t time) */ sysret_t sys_sem_release(rt_sem_t sem) { - return rt_sem_release(sem); + sysret_t ret; + rt_sem_t ksem; + + ksem = (rt_sem_t)lwp_user_object_get(lwp_self(), (rt_object_t)sem, RT_Object_Class_Semaphore); + if (!ksem) + { + return -EINVAL; + } + + ret = rt_sem_release(ksem); + lwp_user_object_put((rt_object_t)ksem); + + return ret; } /** @@ -270,7 +296,21 @@ sysret_t sys_mutex_delete(rt_mutex_t mutex) */ sysret_t sys_mutex_take(rt_mutex_t mutex, rt_int32_t time) { - return rt_mutex_take_interruptible(mutex, time); + sysret_t ret; + rt_mutex_t kmutex; + + kmutex = (rt_mutex_t)lwp_user_object_get(lwp_self(), (rt_object_t)mutex, RT_Object_Class_Mutex); + if (!kmutex) + { + return -EINVAL; + } + + rt_thread_self()->pending_object = (rt_object_t)kmutex; + ret = rt_mutex_take_interruptible(kmutex, time); + rt_thread_self()->pending_object = RT_NULL; + lwp_user_object_put((rt_object_t)kmutex); + + return ret; } /** @@ -294,7 +334,19 @@ sysret_t sys_mutex_take(rt_mutex_t mutex, rt_int32_t time) */ sysret_t sys_mutex_release(rt_mutex_t mutex) { - return rt_mutex_release(mutex); + sysret_t ret; + rt_mutex_t kmutex; + + kmutex = (rt_mutex_t)lwp_user_object_get(lwp_self(), (rt_object_t)mutex, RT_Object_Class_Mutex); + if (!kmutex) + { + return -EINVAL; + } + + ret = rt_mutex_release(kmutex); + lwp_user_object_put((rt_object_t)kmutex); + + return ret; } /** @@ -412,7 +464,19 @@ sysret_t sys_event_delete(rt_event_t event) */ sysret_t sys_event_send(rt_event_t event, rt_uint32_t set) { - return rt_event_send(event, set); + sysret_t ret; + rt_event_t kevent; + + kevent = (rt_event_t)lwp_user_object_get(lwp_self(), (rt_object_t)event, RT_Object_Class_Event); + if (!kevent) + { + return -EINVAL; + } + + ret = rt_event_send(kevent, set); + lwp_user_object_put((rt_object_t)kevent); + + return ret; } /** @@ -463,18 +527,30 @@ sysret_t sys_event_recv(rt_event_t event, { int ret = 0; rt_uint32_t krecved; + rt_event_t kevent; + + kevent = (rt_event_t)lwp_user_object_get(lwp_self(), (rt_object_t)event, RT_Object_Class_Event); + if (!kevent) + { + return -EINVAL; + } if ((recved != NULL) && !lwp_user_accessable((void *)recved, sizeof(rt_uint32_t *))) { + lwp_user_object_put((rt_object_t)kevent); return -EFAULT; } - ret = rt_event_recv(event, set, opt, timeout, &krecved); + rt_thread_self()->pending_object = (rt_object_t)kevent; + ret = rt_event_recv(kevent, set, opt, timeout, &krecved); + rt_thread_self()->pending_object = RT_NULL; if ((ret == RT_EOK) && recved) { lwp_put_to_user((void *)recved, &krecved, sizeof(rt_uint32_t *)); } + lwp_user_object_put((rt_object_t)kevent); + return ret; } @@ -600,7 +676,19 @@ sysret_t sys_mb_delete(rt_mailbox_t mb) */ sysret_t sys_mb_send(rt_mailbox_t mb, rt_ubase_t value) { - return rt_mb_send(mb, value); + sysret_t ret; + rt_mailbox_t kmb; + + kmb = (rt_mailbox_t)lwp_user_object_get(lwp_self(), (rt_object_t)mb, RT_Object_Class_MailBox); + if (!kmb) + { + return -EINVAL; + } + + ret = rt_mb_send(kmb, value); + lwp_user_object_put((rt_object_t)kmb); + + return ret; } /** @@ -639,7 +727,21 @@ sysret_t sys_mb_send_wait(rt_mailbox_t mb, rt_ubase_t value, rt_int32_t timeout) { - return rt_mb_send_wait(mb, value, timeout); + sysret_t ret; + rt_mailbox_t kmb; + + kmb = (rt_mailbox_t)lwp_user_object_get(lwp_self(), (rt_object_t)mb, RT_Object_Class_MailBox); + if (!kmb) + { + return -EINVAL; + } + + rt_thread_self()->pending_object = (rt_object_t)kmb; + ret = rt_mb_send_wait(kmb, value, timeout); + rt_thread_self()->pending_object = RT_NULL; + lwp_user_object_put((rt_object_t)kmb); + + return ret; } /** @@ -674,25 +776,37 @@ sysret_t sys_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout) { int ret = 0; rt_ubase_t *kvalue; + rt_mailbox_t kmb; + + kmb = (rt_mailbox_t)lwp_user_object_get(lwp_self(), (rt_object_t)mb, RT_Object_Class_MailBox); + if (!kmb) + { + return -EINVAL; + } if (!lwp_user_accessable((void *)value, sizeof(rt_ubase_t *))) { + lwp_user_object_put((rt_object_t)kmb); return -EFAULT; } kvalue = kmem_get(sizeof(rt_ubase_t *)); if (kvalue == RT_NULL) { + lwp_user_object_put((rt_object_t)kmb); return -ENOMEM; } - ret = rt_mb_recv(mb, (rt_ubase_t *)kvalue, timeout); + rt_thread_self()->pending_object = (rt_object_t)kmb; + ret = rt_mb_recv(kmb, (rt_ubase_t *)kvalue, timeout); + rt_thread_self()->pending_object = RT_NULL; if (ret == RT_EOK) { lwp_put_to_user(value, kvalue, sizeof(rt_ubase_t *)); } kmem_put(kvalue); + lwp_user_object_put((rt_object_t)kmb); return ret; } @@ -874,27 +988,40 @@ sysret_t sys_mq_send(rt_mq_t mq, void *buffer, rt_size_t size) { int ret = 0; void *kbuffer = RT_NULL; + rt_mq_t kmq; + + kmq = (rt_mq_t)lwp_user_object_get(lwp_self(), (rt_object_t)mq, RT_Object_Class_MessageQueue); + if (!kmq) + { + return -EINVAL; + } if (!lwp_user_accessable((void *)buffer, size)) { + lwp_user_object_put((rt_object_t)kmq); return -EFAULT; } kbuffer = kmem_get(size); if (kbuffer == RT_NULL) { + lwp_user_object_put((rt_object_t)kmq); return -ENOMEM; } if (lwp_get_from_user(kbuffer, buffer, size) != size) { kmem_put(kbuffer); + lwp_user_object_put((rt_object_t)kmq); return -EINVAL; } - ret = rt_mq_send(mq, kbuffer, size); + rt_thread_self()->pending_object = (rt_object_t)kmq; + ret = rt_mq_send(kmq, kbuffer, size); + rt_thread_self()->pending_object = RT_NULL; kmem_put(kbuffer); + lwp_user_object_put((rt_object_t)kmq); return ret; } @@ -931,27 +1058,38 @@ sysret_t sys_mq_urgent(rt_mq_t mq, void *buffer, rt_size_t size) { int ret = 0; void *kbuffer = RT_NULL; + rt_mq_t kmq; + + kmq = (rt_mq_t)lwp_user_object_get(lwp_self(), (rt_object_t)mq, RT_Object_Class_MessageQueue); + if (!kmq) + { + return -EINVAL; + } if (!lwp_user_accessable((void *)buffer, size)) { + lwp_user_object_put((rt_object_t)kmq); return -EFAULT; } kbuffer = kmem_get(size); if (kbuffer == RT_NULL) { + lwp_user_object_put((rt_object_t)kmq); return -ENOMEM; } if (lwp_get_from_user(kbuffer, buffer, size) != size) { kmem_put(kbuffer); + lwp_user_object_put((rt_object_t)kmq); return -EINVAL; } - ret = rt_mq_urgent(mq, kbuffer, size); + ret = rt_mq_urgent(kmq, kbuffer, size); kmem_put(kbuffer); + lwp_user_object_put((rt_object_t)kmq); return ret; } @@ -993,35 +1131,41 @@ sysret_t sys_mq_recv(rt_mq_t mq, { int ret = 0; void *kbuffer = RT_NULL; + rt_mq_t kmq; + + kmq = (rt_mq_t)lwp_user_object_get(lwp_self(), (rt_object_t)mq, RT_Object_Class_MessageQueue); + if (!kmq) + { + return -EINVAL; + } if (!lwp_user_accessable((void *)buffer, size)) { + lwp_user_object_put((rt_object_t)kmq); return -EFAULT; } kbuffer = kmem_get(size); if (kbuffer == RT_NULL) { + lwp_user_object_put((rt_object_t)kmq); return -ENOMEM; } - ret = rt_mq_recv(mq, kbuffer, size, timeout); + rt_thread_self()->pending_object = (rt_object_t)kmq; + ret = rt_mq_recv(kmq, kbuffer, size, timeout); + rt_thread_self()->pending_object = RT_NULL; if (ret > 0) { lwp_put_to_user((void *)buffer, (void *)kbuffer, ret); } kmem_put(kbuffer); + lwp_user_object_put((rt_object_t)kmq); return ret; } -static void timer_timeout_callback(void *parameter) -{ - rt_sem_t sem = (rt_sem_t)parameter; - rt_sem_release(sem); -} - /** * @brief Creates a timer. * @@ -1058,30 +1202,61 @@ rt_timer_t sys_rt_timer_create(const char *name, int len = 0; char *kname = RT_NULL; rt_timer_t timer = RT_NULL; + rt_sem_t ksem; + void *callback_data; + + ksem = (rt_sem_t)lwp_user_object_get(lwp_self(), (rt_object_t)data, + RT_Object_Class_Semaphore); + if (!ksem) + { + rt_set_errno(EINVAL); + return RT_NULL; + } + + /* The timer owns one semaphore reference until SET_PARM or destruction. + * The callback data serializes release with parameter replacement. */ + callback_data = lwp_timer_callback_data_alloc(ksem); + if (!callback_data) + { + lwp_user_object_put((rt_object_t)ksem); + rt_set_errno(ENOMEM); + return RT_NULL; + } len = lwp_user_strlen(name); if (len <= 0) { + lwp_timer_callback_data_free(callback_data); return RT_NULL; } kname = (char *)kmem_get(len + 1); if (!kname) { + lwp_timer_callback_data_free(callback_data); return RT_NULL; } if (lwp_get_from_user(kname, (void *)name, len + 1) != (len + 1)) { kmem_put(kname); + lwp_timer_callback_data_free(callback_data); + return RT_NULL; + } + + timer = rt_timer_create(kname, lwp_timer_callback, callback_data, time, flag); + if (!timer) + { + kmem_put(kname); + lwp_timer_callback_data_free(callback_data); return RT_NULL; } - timer = rt_timer_create(kname, timer_timeout_callback, (void *)data, time, flag); if (lwp_user_object_add(lwp_self(), (rt_object_t)timer) != 0) { rt_timer_delete(timer); - timer = NULL; + lwp_timer_callback_data_free(callback_data); + timer = RT_NULL; } kmem_put(kname); @@ -1134,7 +1309,19 @@ sysret_t sys_rt_timer_delete(rt_timer_t timer) */ sysret_t sys_rt_timer_start(rt_timer_t timer) { - return rt_timer_start(timer); + sysret_t ret; + rt_timer_t ktimer; + + ktimer = (rt_timer_t)lwp_user_object_get(lwp_self(), (rt_object_t)timer, RT_Object_Class_Timer); + if (!ktimer) + { + return -EINVAL; + } + + ret = rt_timer_start(ktimer); + lwp_user_object_put((rt_object_t)ktimer); + + return ret; } /** @@ -1159,7 +1346,19 @@ sysret_t sys_rt_timer_start(rt_timer_t timer) */ sysret_t sys_rt_timer_stop(rt_timer_t timer) { - return rt_timer_stop(timer); + sysret_t ret; + rt_timer_t ktimer; + + ktimer = (rt_timer_t)lwp_user_object_get(lwp_self(), (rt_object_t)timer, RT_Object_Class_Timer); + if (!ktimer) + { + return -EINVAL; + } + + ret = rt_timer_stop(ktimer); + lwp_user_object_put((rt_object_t)ktimer); + + return ret; } /** @@ -1187,7 +1386,118 @@ sysret_t sys_rt_timer_stop(rt_timer_t timer) */ sysret_t sys_rt_timer_control(rt_timer_t timer, int cmd, void *arg) { - return rt_timer_control(timer, cmd, arg); + sysret_t ret = -EINVAL; + rt_timer_t ktimer; + rt_tick_t tick = 0; + rt_uint32_t state = 0; + void *value = RT_NULL; + void *karg = RT_NULL; + rt_sem_t ksem; + rt_sem_t old_sem; + size_t copy_len = 0; + rt_bool_t copy_in = RT_FALSE; + rt_bool_t copy_out = RT_FALSE; + + ktimer = (rt_timer_t)lwp_user_object_get(lwp_self(), (rt_object_t)timer, RT_Object_Class_Timer); + if (!ktimer) + { + return -EINVAL; + } + + if (cmd == RT_TIMER_CTRL_SET_PARM) + { + ksem = (rt_sem_t)lwp_user_object_get(lwp_self(), (rt_object_t)arg, + RT_Object_Class_Semaphore); + if (!ksem) + { + ret = -EINVAL; + goto __exit; + } + + old_sem = lwp_timer_callback_data_swap(ktimer, ksem); + lwp_user_object_put((rt_object_t)old_sem); + ret = RT_EOK; + goto __exit; + } + else if (cmd == RT_TIMER_CTRL_GET_PARM) + { + value = lwp_timer_callback_data_get(ktimer); + if (!arg || lwp_put_to_user(arg, &value, sizeof(value)) != sizeof(value)) + { + ret = -EFAULT; + } + else + { + ret = RT_EOK; + } + goto __exit; + } + + /* never hand the raw user pointer to rt_timer_control(): it dereferences + * arg for GET_TIME/GET_STATE/GET_REMAIN_TIME and reads it for SET_TIME. + * Marshal the argument through a kernel local instead; reject SET_FUNC + * and GET_FUNC so userspace cannot supply or read kernel text. */ + switch (cmd) + { + case RT_TIMER_CTRL_SET_TIME: + karg = &tick; + copy_len = sizeof(tick); + copy_in = RT_TRUE; + break; + case RT_TIMER_CTRL_GET_TIME: + case RT_TIMER_CTRL_GET_REMAIN_TIME: + karg = &tick; + copy_len = sizeof(tick); + copy_out = RT_TRUE; + break; + case RT_TIMER_CTRL_GET_STATE: + karg = &state; + copy_len = sizeof(state); + copy_out = RT_TRUE; + break; + case RT_TIMER_CTRL_SET_ONESHOT: + case RT_TIMER_CTRL_SET_PERIODIC: + karg = RT_NULL; + break; + case RT_TIMER_CTRL_SET_FUNC: + case RT_TIMER_CTRL_GET_FUNC: + /* do not accept a user callback or leak a kernel function address */ + ret = -EINVAL; + goto __exit; + default: + ret = -EINVAL; + goto __exit; + } + + if (copy_in) + { + if (!arg || lwp_get_from_user(karg, arg, copy_len) != copy_len) + { + ret = -EFAULT; + goto __exit; + } + } + + if (cmd == RT_TIMER_CTRL_SET_TIME && tick >= RT_TICK_MAX / 2) + { + ret = -EINVAL; + goto __exit; + } + + ret = rt_timer_control(ktimer, cmd, karg); + + if (ret == RT_EOK && copy_out) + { + if (!arg || lwp_put_to_user(arg, karg, copy_len) != copy_len) + { + ret = -EFAULT; + } + } + +__exit: + lwp_user_object_put((rt_object_t)ktimer); + + return ret; } /* MUSL compatible */ diff --git a/include/rtdef.h b/include/rtdef.h index 5e243a97662e..99116efe6b58 100644 --- a/include/rtdef.h +++ b/include/rtdef.h @@ -304,13 +304,20 @@ struct rt_object #endif /* RT_USING_MODULE */ #ifdef RT_USING_SMART - rt_atomic_t lwp_ref_count; /**< ref count for lwp */ -#endif /* RT_USING_SMART */ + rt_atomic_t lwp_ref_count; /**< ref count for lwp */ + rt_atomic_t lwp_owner_count; /**< number of LWPs owning this object */ + rt_atomic_t lwp_flags; /**< RT_LWP_OBJECT_FLAG_* */ +#endif /* RT_USING_SMART */ rt_list_t list; /**< list node of kernel object */ }; typedef struct rt_object *rt_object_t; /**< Type for kernel objects. */ +#ifdef RT_USING_SMART +/** last LWP owner has closed the object. */ +#define RT_LWP_OBJECT_FLAG_CLOSING 0x01 +#endif /* RT_USING_SMART */ + /** * iterator of rt_object_for_each() * diff --git a/src/ipc.c b/src/ipc.c index 0a690d65b38b..95b9a6cbded3 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -56,6 +56,14 @@ #include #define GET_MESSAGEBYTE_ADDR(msg) ((struct rt_mq_message *)msg + 1) + +#ifdef RT_USING_SMART +static rt_bool_t _ipc_object_is_closing(rt_object_t object) +{ + return (rt_atomic_load(&object->lwp_flags) & RT_LWP_OBJECT_FLAG_CLOSING) != 0; +} +#endif /* RT_USING_SMART */ + #if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR) extern void (*rt_object_trytake_hook)(struct rt_object *object); extern void (*rt_object_take_hook)(struct rt_object *object); @@ -574,6 +582,14 @@ static rt_err_t _rt_sem_take(rt_sem_t sem, rt_int32_t timeout, int suspend_flag) level = rt_spin_lock_irqsave(&(sem->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&sem->parent.parent)) + { + rt_spin_unlock_irqrestore(&(sem->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + LOG_D("thread %s take sem:%s, which value is: %d", rt_thread_self()->parent.name, sem->parent.parent.name, @@ -1339,6 +1355,14 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend rt_spin_lock(&(mutex->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mutex->parent.parent)) + { + rt_spin_unlock(&(mutex->spinlock)); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mutex->parent.parent))); LOG_D("mutex_take: current thread %s, hold: %d", @@ -2157,6 +2181,14 @@ static rt_err_t _rt_event_recv(rt_event_t event, level = rt_spin_lock_irqsave(&(event->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&event->parent.parent)) + { + rt_spin_unlock_irqrestore(&(event->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + /* check event set */ if (option & RT_EVENT_FLAG_AND) { @@ -2628,6 +2660,14 @@ static rt_err_t _rt_mb_send_wait(rt_mailbox_t mb, /* disable interrupt */ level = rt_spin_lock_irqsave(&(mb->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mb->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mb->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + /* for non-blocking call */ if (mb->entry == mb->size && timeout == 0) { @@ -2638,6 +2678,13 @@ static rt_err_t _rt_mb_send_wait(rt_mailbox_t mb, /* mailbox is full */ while (mb->entry == mb->size) { +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mb->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mb->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ /* reset error number in thread */ thread->error = -RT_EINTR; @@ -2689,6 +2736,14 @@ static rt_err_t _rt_mb_send_wait(rt_mailbox_t mb, level = rt_spin_lock_irqsave(&(mb->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mb->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mb->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + /* if it's not waiting forever and then re-calculate timeout tick */ if (timeout > 0) { @@ -2910,6 +2965,14 @@ static rt_err_t _rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeo level = rt_spin_lock_irqsave(&(mb->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mb->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mb->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + /* for non-blocking call */ if (mb->entry == 0 && timeout == 0) { @@ -2921,6 +2984,13 @@ static rt_err_t _rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeo /* mailbox is empty */ while (mb->entry == 0) { +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mb->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mb->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ /* reset error number in thread */ thread->error = -RT_EINTR; @@ -2973,6 +3043,14 @@ static rt_err_t _rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeo } level = rt_spin_lock_irqsave(&(mb->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mb->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mb->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + /* if it's not waiting forever and then re-calculate timeout tick */ if (timeout > 0) { @@ -3462,6 +3540,14 @@ static rt_err_t _rt_mq_send_wait(rt_mq_t mq, level = rt_spin_lock_irqsave(&(mq->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mq->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mq->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + /* get a free list, there must be an empty item */ msg = (struct rt_mq_message *)mq->msg_queue_free; /* for non-blocking call */ @@ -3475,6 +3561,13 @@ static rt_err_t _rt_mq_send_wait(rt_mq_t mq, /* message queue is full */ while ((msg = (struct rt_mq_message *)mq->msg_queue_free) == RT_NULL) { +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mq->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mq->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ /* reset error number in thread */ thread->error = -RT_EINTR; @@ -3525,6 +3618,14 @@ static rt_err_t _rt_mq_send_wait(rt_mq_t mq, } level = rt_spin_lock_irqsave(&(mq->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mq->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mq->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + /* if it's not waiting forever and then re-calculate timeout tick */ if (timeout > 0) { @@ -3552,6 +3653,16 @@ static rt_err_t _rt_mq_send_wait(rt_mq_t mq, /* disable interrupt */ level = rt_spin_lock_irqsave(&(mq->spinlock)); + +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mq->parent.parent)) + { + msg->next = (struct rt_mq_message *)mq->msg_queue_free; + mq->msg_queue_free = msg; + rt_spin_unlock_irqrestore(&(mq->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ #ifdef RT_USING_MESSAGEQUEUE_PRIORITY msg->prio = prio; if (mq->msg_queue_head == RT_NULL) @@ -3734,6 +3845,14 @@ rt_err_t rt_mq_urgent(rt_mq_t mq, const void *buffer, rt_size_t size) level = rt_spin_lock_irqsave(&(mq->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mq->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mq->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + /* get a free list, there must be an empty item */ msg = (struct rt_mq_message *)mq->msg_queue_free; /* message queue is full */ @@ -3857,6 +3976,14 @@ static rt_ssize_t _rt_mq_recv(rt_mq_t mq, level = rt_spin_lock_irqsave(&(mq->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mq->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mq->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + /* for non-blocking call */ if (mq->entry == 0 && timeout == 0) { @@ -3868,6 +3995,13 @@ static rt_ssize_t _rt_mq_recv(rt_mq_t mq, /* message queue is empty */ while (mq->entry == 0) { +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mq->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mq->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ /* reset error number in thread */ thread->error = -RT_EINTR; @@ -3916,12 +4050,21 @@ static rt_ssize_t _rt_mq_recv(rt_mq_t mq, /* recv message */ if (thread->error != RT_EOK) { - /* return error */ - return thread->error; + /* rt_susp_list_resume_all() preserves positive delete errors; + * receive returns a negative error, never a bogus message length. */ + return thread->error > 0 ? -thread->error : thread->error; } level = rt_spin_lock_irqsave(&(mq->spinlock)); +#ifdef RT_USING_SMART + if (_ipc_object_is_closing(&mq->parent.parent)) + { + rt_spin_unlock_irqrestore(&(mq->spinlock), level); + return -RT_ERROR; + } +#endif /* RT_USING_SMART */ + /* if it's not waiting forever and then re-calculate timeout tick */ if (timeout > 0) { diff --git a/src/object.c b/src/object.c index 373148221d19..c97221312e51 100644 --- a/src/object.c +++ b/src/object.c @@ -408,6 +408,11 @@ void rt_object_init(struct rt_object *object, /* initialize object's parameters */ /* set object type to static */ object->type = type | RT_Object_Class_Static; +#ifdef RT_USING_SMART + rt_atomic_store(&object->lwp_ref_count, 0); + rt_atomic_store(&object->lwp_owner_count, 0); + rt_atomic_store(&object->lwp_flags, 0); +#endif /* RT_USING_SMART */ #if RT_NAME_MAX > 0 if (name) { @@ -523,6 +528,12 @@ rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name) /* set object flag */ object->flag = 0; +#ifdef RT_USING_SMART + rt_atomic_store(&object->lwp_ref_count, 0); + rt_atomic_store(&object->lwp_owner_count, 0); + rt_atomic_store(&object->lwp_flags, 0); +#endif /* RT_USING_SMART */ + #if RT_NAME_MAX > 0 if (name) { diff --git a/src/thread.c b/src/thread.c index 8a557a128d67..a813bd8a8b33 100644 --- a/src/thread.c +++ b/src/thread.c @@ -130,6 +130,15 @@ static void _thread_exit(void) _thread_detach_from_mutex(thread); +#ifdef RT_USING_SMART + if (thread->pending_object) + { + extern void lwp_user_object_put(rt_object_t object); + lwp_user_object_put(thread->pending_object); + thread->pending_object = RT_NULL; + } +#endif + /* insert to defunct thread list */ rt_thread_defunct_enqueue(thread); @@ -520,6 +529,15 @@ static rt_err_t _thread_detach(rt_thread_t thread) _thread_detach_from_mutex(thread); +#ifdef RT_USING_SMART + if (thread->pending_object) + { + extern void lwp_user_object_put(rt_object_t object); + lwp_user_object_put(thread->pending_object); + thread->pending_object = RT_NULL; + } +#endif + /* insert to defunct thread list */ rt_thread_defunct_enqueue(thread);