RT-Thread Version
v5.2.2
Hardware Type/Architectures
N/A
Develop Toolchain
Other
Describe the bug
sys_getaddrinfo trusts nested user pointer and can write outside validated user memory
Summary
I identified an insufficient user-pointer validation issue in the sys_getaddrinfo system call in RT-Thread Smart.
When ARCH_MM_MMU is enabled, sys_getaddrinfo() validates that the outer res pointer is user-accessible. However, after a successful name lookup it reads res->ai_addr directly from that user-controlled structure and passes it to sockaddr_tomusl(). sockaddr_tomusl() then writes a struct musl_sockaddr result through that nested pointer without checking that the nested destination is valid user memory.
This gives an unprivileged user process control over the kernel write destination used for the returned socket address. If the nested pointer targets mapped kernel or otherwise sensitive memory, the syscall can perform an unauthorized write. If it targets unmapped or faulting memory, the direct kernel write can crash the system, causing a denial of service.
Vulnerable Code Location
The vulnerable syscall is registered in the LWP syscall table:
components/lwp/lwp_syscall.c:11085 SYSCALL_NET(SYSCALL_SIGN(sys_getaddrinfo)),
The affected code is in:
components/lwp/lwp_syscall.c:7051 sys_getaddrinfo(...)
components/lwp/lwp_syscall.c:7163 sockaddr_tomusl(k_res->ai_addr, res->ai_addr)
components/lwp/lwp_syscall.c:314 sockaddr_tomusl(...)
components/lwp/lwp_sys_socket.h:105 struct musl_sockaddr
The important data flow is:
user-controlled res
-> sys_getaddrinfo(..., struct musl_addrinfo *res)
-> lwp_user_accessable(res, sizeof(*res)) checks only the outer object
-> res->ai_addr is read directly from user memory
-> sockaddr_tomusl(k_res->ai_addr, res->ai_addr)
-> std->sa_family write and memcpy(std->sa_data, ...)
-> attacker-selected destination write or kernel fault
Relevant code:
struct musl_addrinfo
{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct musl_sockaddr *ai_addr;
char *ai_canonname;
struct musl_addrinfo *ai_next;
};
sysret_t sys_getaddrinfo(const char *nodename,
const char *servname,
const struct musl_addrinfo *hints,
struct musl_addrinfo *res)
{
...
#ifdef ARCH_MM_MMU
if (!lwp_user_accessable((void *)res, sizeof(*res)))
{
SET_ERRNO(EFAULT);
goto exit;
}
#endif
...
ret = sal_getaddrinfo(k_nodename, k_servname, k_hints, &k_res);
if (ret == 0)
{
/* res is validated, but res->ai_addr is not. */
sockaddr_tomusl(k_res->ai_addr, res->ai_addr);
res->ai_addrlen = k_res->ai_addrlen;
res->ai_family = k_res->ai_family;
res->ai_flags = k_res->ai_flags;
res->ai_next = NULL;
...
}
...
}
sockaddr_tomusl() only checks for NULL. It does not validate that std points to writable user memory and it does not use lwp_put_to_user():
static void sockaddr_tomusl(const struct sockaddr *lwip, struct musl_sockaddr *std)
{
if (std && lwip)
{
std->sa_family = (uint16_t) lwip->sa_family;
memcpy(std->sa_data, lwip->sa_data, sizeof(std->sa_data));
}
}
The destination type is 16 bytes:
struct musl_sockaddr
{
uint16_t sa_family;
char sa_data[14];
};
Vulnerability Description
The syscall validates only the top-level res pointer:
lwp_user_accessable((void *)res, sizeof(*res))
That check proves only that the struct musl_addrinfo object itself is in accessible user memory. It does not prove that the pointer stored inside res->ai_addr is safe to write to.
After sal_getaddrinfo() succeeds, the kernel directly evaluates res->ai_addr and uses it as the output destination for the resolved socket address:
sockaddr_tomusl(k_res->ai_addr, res->ai_addr);
Because res is user-controlled, res->ai_addr is also user-controlled. A malicious process can set it to:
- A kernel or sensitive memory address that should not be writable from user mode, causing an unauthorized kernel write.
- An unmapped or invalid address, causing a kernel fault and denial of service.
- Another user process's mapped memory, if such mappings are reachable in the current address-space configuration.
The bug is similar in shape to other nested-pointer syscall bugs: checking the outer structure is not enough. Each user-supplied pointer contained inside that structure must be copied into kernel memory and validated before use, and writes back to user space should go through the kernel's user-copy helpers.
Steps to Reproduce
I have not reproduced this on physical RT-Thread Smart hardware. The issue is visible from source-level inspection of the syscall path.
Required configuration:
RT_USING_SMART or RT_USING_LWP enabled
ARCH_MM_MMU enabled
RT_USING_SAL enabled
SAL_USING_POSIX enabled
Several Smart/MMU board configurations in the checked tree enable the relevant pieces, including:
bsp/nxp/imx/imx6ull-smart/rtconfig.h
bsp/k230/rtconfig.h
bsp/cvitek/cv18xx_risc-v/rtconfig.h
bsp/rockchip/rk3500/rtconfig.h
bsp/raspberry-pi/raspi-dm2.0/rtconfig.h
Conceptual trigger from an unprivileged process:
struct musl_addrinfo res;
memset(&res, 0, sizeof(res));
/*
* The outer object &res is a valid user pointer, so sys_getaddrinfo()
* accepts it. The nested pointer is attacker-controlled.
*/
res.ai_addr = (struct musl_sockaddr *)ATTACKER_CHOSEN_ADDRESS;
sys_getaddrinfo("127.0.0.1", "80", NULL, &res);
If ATTACKER_CHOSEN_ADDRESS is writable kernel memory, the syscall writes the returned address family and 14 bytes of address data there. If it is invalid or unmapped, the same direct write can fault in kernel context.
Expected safe behavior:
sys_getaddrinfo() should reject an inaccessible nested res->ai_addr pointer with EFAULT,
or copy the result through a validated kernel temporary and lwp_put_to_user().
Current behavior:
sys_getaddrinfo() validates only res, then writes through res->ai_addr directly.
Impact
This vulnerability can allow an unprivileged process to influence where the kernel writes the resolved socket address returned by sys_getaddrinfo().
Potential impacts include:
Unauthorized memory write:
The attacker controls res->ai_addr, and sockaddr_tomusl() writes a 16-byte
musl_sockaddr result to that address.
Denial of service:
If res->ai_addr points to unmapped or otherwise faulting memory, the direct
kernel write can crash or destabilize the system.
Privilege escalation risk:
On targets where user-controlled syscall writes can reach sensitive kernel
state, the write may corrupt kernel objects or control data.
The exact exploitability depends on the target MMU mapping, kernel address exposure, and RT-Thread Smart memory protection configuration. The core issue is that the syscall crosses the user/kernel boundary with a nested attacker-controlled pointer and writes through it without validation.
Suggested Fix
Do not read or write nested user pointers directly from res.
One safer pattern is:
1. Copy the outer musl_addrinfo from user memory into a kernel temporary.
2. Validate copied_res.ai_addr with lwp_user_accessable(copied_res.ai_addr, sizeof(struct musl_sockaddr)).
3. Convert the kernel sockaddr into a kernel musl_sockaddr temporary.
4. Use lwp_put_to_user(copied_res.ai_addr, &tmp_sockaddr, sizeof(tmp_sockaddr)).
5. Use lwp_put_to_user(res, &tmp_addrinfo, sizeof(tmp_addrinfo)) for the outer result fields.
The same pattern should also be used for reading hints: copy it once with lwp_get_from_user() and use the kernel copy, instead of repeatedly reading fields from the user pointer.
Environment
Initial RT-Thread commit checked: c39e92f4c1
Version: 5.2.2
Affected file: components/lwp/lwp_syscall.c
Affected syscall: sys_getaddrinfo
Affected configuration: RT-Thread Smart / LWP with ARCH_MM_MMU and SAL POSIX networking
Target hardware: not tested on board yet
Verification: source-level syscall data-flow review
Other additional context
No response
RT-Thread Version
v5.2.2
Hardware Type/Architectures
N/A
Develop Toolchain
Other
Describe the bug
sys_getaddrinfo trusts nested user pointer and can write outside validated user memory
Summary
I identified an insufficient user-pointer validation issue in the
sys_getaddrinfosystem call in RT-Thread Smart.When
ARCH_MM_MMUis enabled,sys_getaddrinfo()validates that the outerrespointer is user-accessible. However, after a successful name lookup it readsres->ai_addrdirectly from that user-controlled structure and passes it tosockaddr_tomusl().sockaddr_tomusl()then writes astruct musl_sockaddrresult through that nested pointer without checking that the nested destination is valid user memory.This gives an unprivileged user process control over the kernel write destination used for the returned socket address. If the nested pointer targets mapped kernel or otherwise sensitive memory, the syscall can perform an unauthorized write. If it targets unmapped or faulting memory, the direct kernel write can crash the system, causing a denial of service.
Vulnerable Code Location
The vulnerable syscall is registered in the LWP syscall table:
The affected code is in:
The important data flow is:
Relevant code:
sockaddr_tomusl()only checks for NULL. It does not validate thatstdpoints to writable user memory and it does not uselwp_put_to_user():The destination type is 16 bytes:
Vulnerability Description
The syscall validates only the top-level
respointer:That check proves only that the
struct musl_addrinfoobject itself is in accessible user memory. It does not prove that the pointer stored insideres->ai_addris safe to write to.After
sal_getaddrinfo()succeeds, the kernel directly evaluatesres->ai_addrand uses it as the output destination for the resolved socket address:Because
resis user-controlled,res->ai_addris also user-controlled. A malicious process can set it to:The bug is similar in shape to other nested-pointer syscall bugs: checking the outer structure is not enough. Each user-supplied pointer contained inside that structure must be copied into kernel memory and validated before use, and writes back to user space should go through the kernel's user-copy helpers.
Steps to Reproduce
I have not reproduced this on physical RT-Thread Smart hardware. The issue is visible from source-level inspection of the syscall path.
Required configuration:
Several Smart/MMU board configurations in the checked tree enable the relevant pieces, including:
Conceptual trigger from an unprivileged process:
If
ATTACKER_CHOSEN_ADDRESSis writable kernel memory, the syscall writes the returned address family and 14 bytes of address data there. If it is invalid or unmapped, the same direct write can fault in kernel context.Expected safe behavior:
Current behavior:
Impact
This vulnerability can allow an unprivileged process to influence where the kernel writes the resolved socket address returned by
sys_getaddrinfo().Potential impacts include:
The exact exploitability depends on the target MMU mapping, kernel address exposure, and RT-Thread Smart memory protection configuration. The core issue is that the syscall crosses the user/kernel boundary with a nested attacker-controlled pointer and writes through it without validation.
Suggested Fix
Do not read or write nested user pointers directly from
res.One safer pattern is:
The same pattern should also be used for reading
hints: copy it once withlwp_get_from_user()and use the kernel copy, instead of repeatedly reading fields from the user pointer.Environment
Other additional context
No response