-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
RT-Thread Version
5.2.2
Hardware Type/Architectures
raspberry-pico
Develop Toolchain
GCC
Describe the bug
This is in reference to #10943
Description
The recent merge replaced RT_ASSERT and rt_memcpy with rt_strncpy in object.c to handle name overflows. While this prevents buffer overflows and boot loops, it introduces a critical logical vulnerability in the kernel: Object Shadowing.
Because the RT-Thread Object Container uses a LIFO (Last-In, First-Out) insertion policy, silently truncating a name causes new objects to "shadow" (hide) previously created objects with the same prefix. This leads to a situation where looking up an object by name returns the newest object, not necessarily the intended one.
A scan of possible name collisions on the repository finds a number of bsp and sdk files contain long object names that might have latent issues the new rt_strncpy method could obscure. There are also several UTEST tests that will now behave strangely if the RT_NAME_MAX setting is not high enough. Previously any misconfiguration would fail on the RT_ASSERT and be easy to debug, now the effects could be hard to predict or reproduce.
Testing
I wrote a small application that creates two semaphores with long names and then attempts to reach them by name. This was then tested on a raspberry pi pico 2 using the raspberry-pico/RP2350 bsp against the old code with RT_ASSERT and the recently merged code using rt_strncpy.
Test Code
#define TEST_NAME_1 "motor_controller_left"
#define TEST_NAME_2 "motor_controller_right"
void bug_demonstration(void)
{
rt_kprintf("\n--- STARTING TRUNCATION TEST ---\n");
/* 1. Create the "Left" Motor Semaphore */
rt_sem_t sem_left = rt_sem_create(TEST_NAME_1, 0, RT_IPC_FLAG_FIFO);
if (!sem_left) {
rt_kprintf("Failed to create Left semaphore\n");
return;
}
/* Cast to (struct rt_object *) to reach the 'name' member */
rt_kprintf("Created: %s (Handle: %p)\n", ((struct rt_object *)sem_left)->name, sem_left);
/* 2. Create the "Right" Motor Semaphore */
rt_sem_t sem_right = rt_sem_create(TEST_NAME_2, 0, RT_IPC_FLAG_FIFO);
if (!sem_right) {
rt_kprintf("Failed to create Right semaphore\n");
return;
}
rt_kprintf("Created: %s (Handle: %p)\n", ((struct rt_object *)sem_right)->name, sem_right);
/* 3. THE BUG: Try to find the "Left" motor (The first one created) */
rt_kprintf("\n--- SEARCHING FOR LEFT MOTOR ---\n");
rt_object_t found_obj = rt_object_find(TEST_NAME_1, RT_Object_Class_Semaphore);
if (found_obj == RT_NULL) {
rt_kprintf("[!] Error: Could not find object by name.\n");
}
else if (found_obj == (rt_object_t)sem_left) {
rt_kprintf("[SUCCESS] System correctly identified the LEFT Motor.\n");
}
else if (found_obj == (rt_object_t)sem_right) {
rt_kprintf("\n[!!! CRITICAL FAILURE !!!]\n");
rt_kprintf("Object Shadowing Detected!\n");
rt_kprintf("We asked for: %s\n", TEST_NAME_1);
rt_kprintf("We received Handle: %p (This is the RIGHT motor!)\n", found_obj);
rt_kprintf("The Left Motor is now unreachable by name.\n");
}
}
/* This macro MUST be outside of any function (Global Scope) */
MSH_CMD_EXPORT(bug_demonstration, Test object name truncation);Results
With the old code the system crashed on the RT_ASSERT as expected, which is predictable and easy to debug. I could simply inspect my memory needs and increase the RT_NAME_MAX if safe or adjust my names to correct this.
Before Merge
msh >bug_demonstration
--- STARTING TRUNCATION TEST ---
[E/kernel.obj] Object name motor_controller_left exceeds RT_NAME_MAX=12, consider increasing RT_NAME_MAX.
(obj_name_len <= RT_NAME_MAX - 1) assertion failed at function:rt_object_allocate, line number:518
[W/kernel.service] rt_hw_backtrace_frame_unwind is not implemented
please use: addr2line -e rtthread.elf -a -f
0x1001ac98[W/kernel.service] rt_hw_backtrace_frame_unwind is not implementedWith the new object.c code, the test completes, but verifies that one of the objects is shadowed by the other. The first object is no longer reachable by name. Listing semaphores confirms there are two with identical names.
After Merge
msh >bug_demonstration
--- STARTING TRUNCATION TEST ---
[E/kernel.obj] Object name motor_controller_left exceeds RT_NAME_MAX=12, consider increasing RT_NAME_MAX.
Created: motor_contr (Handle: 0x20010aa8)
[E/kernel.obj] Object name motor_controller_right exceeds RT_NAME_MAX=12, consider increasing RT_NAME_MAX.
Created: motor_contr (Handle: 0x20010ae8)
--- SEARCHING FOR LEFT MOTOR ---
[!!! CRITICAL FAILURE !!!]
Object Shadowing Detected!
We asked for: motor_controller_left
We received Handle: 0x20010ae8 (This is the RIGHT motor!)
The Left Motor is now unreachable by name.
msh >list sem
semaphore v suspend thread
------------ --- --------------
motor_contr 000 0
motor_contr 000 0
shrx 000 0
stimer 000 1:timer
msh >Conclusions
The related merge should be reverted until further discussion takes place. I have read through the conversations regarding this issue, and there were several good ideas, but they would all require a deal of work, thought, and testing. The original RT_ASSERT behavior, while annoying in production, is the only safe default until then. It forces the developer to acknowledge and fix the configuration mismatch (increasing RT_NAME_MAX or shortening the name) rather than letting the Kernel silently make unsafe guesses about object identity.
Why a Revert is Necessary
You might argue that code breaking "in the wild" is unlikely because existing code already passed the assertions. However, this change creates a problem waiting to happen for future development:
-
Loss of Determinism: An RTOS must be deterministic. If I request an object named "A", I must get "A" or an error. Getting "B" (because "A" was shadowed) violates the fundamental contract of the OS.
-
Invisible Failure: If a developer mistakenly uses a long name, the new code hides the error. The system appears to work but is internally corrupted (Object Shadowing). This turns a simple syntax error into a complex logic issue that may not manifest until the device is in production.