-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
RT-Thread Version
master (latest)
Hardware Type/Architectures
N/A — platform-independent logic bug in components/libc/posix/libdl/
Develop Toolchain
Microsoft VScode
Describe the bug
Bug Description
Summary
When dlopen() is called with a full file path (e.g. "/mnt/sdcard/apps/clock.so"), it passes the full path to dlmodule_find() for lookup. However, during module loading, _dlmodule_set_name() strips the path and extension, storing only the bare filename (e.g. "clock") into module->parent.name. Since dlmodule_find() uses rt_object_find() which matches against object->name, the lookup always fails for previously loaded modules.
This causes:
- Module reloaded on every
dlopen()call — thenref++branch is never reached - Memory leak — duplicate module instances accumulate in RAM
dlclose()cannot properly clean up — eachdlopen()returns a different handle pointing to a different copy
Root Cause Analysis
Step 1: dlopen() passes full path to dlmodule_find()
File: dlopen.c, line 38-44
void *dlopen(const char *filename, int flags)
{
// ...
fullpath = (char *)filename; /* e.g. "/mnt/sdcard/apps/clock.so" */
rt_enter_critical();
module = dlmodule_find(fullpath); // <-- passes full path as-is
if (module != RT_NULL)
{
rt_exit_critical();
module->nref++; // <-- NEVER reached due to this bug
}
else
{
rt_exit_critical();
module = dlmodule_load(fullpath); // <-- always falls through here
}
// ...
}Step 2: dlmodule_find() does direct name matching with no path processing
File: dlmodule.c, line 1130-1142
struct rt_dlmodule *dlmodule_find(const char *name)
{
rt_object_t object;
struct rt_dlmodule *ret = RT_NULL;
object = rt_object_find(name, RT_Object_Class_Module);
// ↑ tries to match name == "/mnt/sdcard/apps/clock.so"
// but no module is registered under that name
if (object) {
ret = (struct rt_dlmodule *) object;
}
return ret;
}Step 3: _dlmodule_set_name() strips path and extension before storing
File: dlmodule.c, line 74-99
static void _dlmodule_set_name(struct rt_dlmodule *module, const char *path)
{
// ...
while (*ptr != '\0')
{
if (*ptr == '/')
first = ptr + 1; // skip to after last '/'
if (*ptr == '.')
end = ptr - 1; // stop before last '.'
ptr ++;
}
// For path "/mnt/sdcard/apps/clock.so":
// → stores "clock" into object->name
}The Mismatch
| Operation | Key used | Value |
|---|---|---|
dlmodule_find() query |
full path | "/mnt/sdcard/apps/clock.so" |
object->name stored by _dlmodule_set_name() |
stripped name | "clock" |
These will never match.
Steps to Reproduce
#include <dlfcn.h>
/* First dlopen — module loaded, name stored as "clock" */
void *h1 = dlopen("/mnt/sdcard/apps/clock.so", 0);
/* Second dlopen — should find existing module and nref++,
but actually loads a second copy because find fails */
void *h2 = dlopen("/mnt/sdcard/apps/clock.so", 0);
/* h1 != h2 — two independent copies of clock.so now in memory */Expected Behavior
The second dlopen() call should find the already-loaded module, increment nref, and return the same module handle.
Actual Behavior
Every dlopen() call with a full path loads a new copy of the module. The nref++ reuse branch in dlopen() is effectively dead code — nref is only ever incremented to 1 inside dlmodule_load() itself (line 753), but never reaches 2+ through the intended dlopen() reuse path.
Suggested Fix
Apply the same path-stripping logic before calling dlmodule_find() in dlopen(). Minimal patch:
/* dlopen.c — extract module name from path before find */
void *dlopen(const char *filename, int flags)
{
struct rt_dlmodule *module;
char module_name[RT_NAME_MAX];
RT_ASSERT(filename != RT_NULL);
/* Strip path and extension — same logic as _dlmodule_set_name() */
{
const char *first, *end, *ptr;
int size;
ptr = first = filename;
end = filename + rt_strlen(filename);
while (*ptr != '\0')
{
if (*ptr == '/')
first = ptr + 1;
if (*ptr == '.')
end = ptr - 1;
ptr++;
}
size = end - first + 1;
if (size > RT_NAME_MAX) size = RT_NAME_MAX;
rt_memset(module_name, 0x00, sizeof(module_name));
rt_strncpy(module_name, first, size);
module_name[RT_NAME_MAX - 1] = '\0';
}
rt_enter_critical();
module = dlmodule_find(module_name); /* now matches object->name */
if (module != RT_NULL)
{
rt_exit_critical();
module->nref++;
}
else
{
rt_exit_critical();
module = dlmodule_load(filename); /* still pass full path for file I/O */
}
return (void *)module;
}Alternatively, the stripping logic could be moved into dlmodule_find() itself, but that may affect other callers (e.g. dlrun() which already passes a bare module name).
Additional Notes
-
Dead code on
dlopen.cline 28: The conditionif (0) //filename[0] != '/'permanently disables the relative-path handling branch. This appears to be a debugging leftover. -
Secondary bug in
_dlmodule_set_name(): The function finds the last.in the entire path string, not just in the filename portion. For paths like/mnt/v1.2/app.so,endwould point into the directory component (v1instead ofapp), producing an incorrect module name.
Other additional context
No response