Junbo-Zheng opened a new pull request, #20128:
URL: https://github.com/apache/nuttx/pull/20128
## Summary
The documented caller contract for `prctl(PR_GET_NAME)` is a buffer of
`CONFIG_TASK_NAME_SIZE` bytes, NUL included (include/sys/prctl.h shows `char
myname[CONFIG_TASK_NAME_SIZE]` as the usage example). However, the
implementation passed `sizeof(tcb->name)`, i.e. `CONFIG_TASK_NAME_SIZE + 1`, to
`strlcpy()`:
strlcpy(name, tcb->name, sizeof(tcb->name));
name[CONFIG_TASK_NAME_SIZE - 1] = '\0';
`tcb->name` is `char[CONFIG_TASK_NAME_SIZE + 1]` and normally holds exactly
`CONFIG_TASK_NAME_SIZE` characters after `nxtask_setup_name()` truncation. In
that case `strlcpy()` copies all of them and writes its terminating NUL at
`name[CONFIG_TASK_NAME_SIZE]` -- one byte past the caller's buffer. The
forced-NUL line below it is a leftover from the `strncpy` era and ran after the
overflow had already happened.
The fix passes `CONFIG_TASK_NAME_SIZE` to `strlcpy()` so the copy is
truncated in bounds (at most `CONFIG_TASK_NAME_SIZE - 1` characters plus NUL),
and drops the stale forced-NUL line.
## Impact
- **Users**: fixes a 1-byte out-of-bounds stack write for any caller that
follows the documented buffer size when querying a task whose name was
truncated to exactly `CONFIG_TASK_NAME_SIZE` characters. The returned name is
now truncated to `CONFIG_TASK_NAME_SIZE - 1` characters plus NUL, matching the
documented contract and Linux behavior.
- **Build**: None.
- **Hardware**: None -- plain libc-level fix, no board specifics.
- **Documentation**: None -- the code now matches the existing contract
documented in include/sys/prctl.h.
- **Security & Compatibility**: closes a memory-corruption defect (single
NUL byte written past a caller-provided buffer). No API change.
## Testing
Simulated (sim:nsh host build on Ubuntu x86-64, `CONFIG_TASK_NAME_SIZE=31`).
Build and run:
```
cmake -B build -DBOARD_CONFIG=sim:nsh -GNinja
cmake --build build -j$(nproc)
echo hello | ./build/nuttx
```
then run "hello" at the NSH prompt.
The test was carried by apps/examples/hello/hello_main.c (scratch only, not
part of this PR); its diff:
```diff
--- a/examples/hello/hello_main.c
+++ b/examples/hello/hello_main.c
@@ -24,6 +24,8 @@
#include <nuttx/config.h>
#include <stdio.h>
+#include <string.h>
+#include <sys/prctl.h>
/****************************************************************************
* Public Functions
@@ -35,6 +37,55 @@
int main(int argc, FAR char *argv[])
{
+ /* Longest-legal task name: exactly CONFIG_TASK_NAME_SIZE chars, the
+ * normal result of nxtask_setup_name() truncation.
+ */
+
+ static const char longname[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+ /* Caller buffer per the documented prctl(PR_GET_NAME) contract, with a
+ * guard byte immediately after it to detect the 1-byte overflow.
+ */
+
+ struct
+ {
+ char buf[CONFIG_TASK_NAME_SIZE];
+ volatile unsigned char guard;
+ } s;
+
+ _Static_assert(sizeof(longname) - 1 > CONFIG_TASK_NAME_SIZE,
+ "test name must exceed CONFIG_TASK_NAME_SIZE");
+
printf("Hello, World!!\n");
+ printf("prctl test: CONFIG_TASK_NAME_SIZE=%d\n", CONFIG_TASK_NAME_SIZE);
+
+ s.guard = 0xaa;
+ s.buf[0] = '\0';
+
+ if (prctl(PR_SET_NAME, (unsigned long)longname) != 0)
+ {
+ printf("prctl test: PR_SET_NAME failed\n");
+ return 1;
+ }
+
+ if (prctl(PR_GET_NAME, (unsigned long)s.buf) != 0)
+ {
+ printf("prctl test: PR_GET_NAME failed\n");
+ return 1;
+ }
+
+ printf("prctl test: guard=0x%02x (expected 0xaa), name len=%zu, "
+ "last char=0x%02x\n",
s.guard, strlen(s.buf), (unsigned char)s.buf[strlen(s.buf)]);
+
+ if (s.guard != 0xaa)
+ {
+ printf("prctl test: FAIL - terminating NUL written 1 byte past "
+ "the caller buffer\n");
+ return 1;
+ }
+
+ printf("prctl test: PASS - caller buffer intact\n");
return 0;
}
```
Before the fix:
```
prctl test: CONFIG_TASK_NAME_SIZE=31
prctl test: guard=0x00 (expected 0xaa), name len=30, last char=0x00
prctl test: FAIL - terminating NUL written 1 byte past the caller buffer
```
After the fix:
```
prctl test: CONFIG_TASK_NAME_SIZE=31
prctl test: guard=0xaa (expected 0xaa), name len=30, last char=0x00
prctl test: PASS - caller buffer intact
```
The before/after outputs were reproduced independently in this session on
the unfixed base (origin/master) and on this branch.
Signed-off-by: Junbo Zheng <[email protected]>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]