Repository navigation
[Bug] wrong impl of vsnprintf #11833
Copy link
Copy link
Open
Labels
KernelPR has src relate codePR has src relate codebugThis PR/issue is a bug in the current code.This PR/issue is a bug in the current code.in progressPR/issue in progress.PR/issue in progress.
Description
RT-Thread Version
5.x
Affected area
Kernel
Hardware/BSP vendor
Not applicable / Other
Architecture
Not applicable / Other
Board and hardware details
Any hardware
Develop Toolchain
GCC
Describe the bug
vsnprintf 实现缺陷
当前的 rt_vsnprintf 实现存在缺陷,尤其是tiny版本。
实现问题
对于当前的实现,%[width].[precision]s当前实现存在错误。
假定输入为 "abcdef":
| 格式化字符串 | 预期 | tiny 实际 | 备注 |
|---|---|---|---|
%3s |
"abcdef" |
"abc" |
3应当是最小宽度,不是最大宽度 |
%-3s |
"abcdef" |
"abc" |
左对齐同上 |
%*s |
"abcdef" |
"" |
宽度参数为 0;零宽度不应禁止输出 |
%.0s |
"" |
"abcdef" |
未处理零精度 |
%.s |
"" |
"abcdef" |
只有点号等价于精度为 0 |
%.*s |
"" |
"abcdef" |
精度参数为 0;动态零精度同样未生效 |
%5.0s |
"·····" |
"abcde" |
应只填充五个空格,实际输出了五个字符串字符 |
%3.5s |
"abcde" |
"abc" |
综合性问题,应由精度限制为五个字符,实际被宽度截成三个 |
%.3s |
"abc",不越界读取 |
越界读取,输出不确定 | 输入为 char src[3] = {'a','b','c'},没有终止符;原版先扫描,再应用精度 |
此外,tiny版本没有处理负数精度的场景,例如 "%.*s", -3, "abcdef",但是被前面的bug拦截住了,恰好正确。
%d的实现也有点问题:
| 格式化字符串 | 预期 | tiny 实际(修复前) | 备注 |
|---|---|---|---|
%.0d, 5 |
"5" |
"" |
精度 0 是"最少 0 位",数值仍应输出;修复前整个数字被吞 |
%.*d, -1, 5 |
"5" |
"" |
负精度应视为"未指定精度";修复前被当成精度 0,同样吞掉数字 |
%.0x, 255 |
"ff" |
"" |
与 %.0d 同一 bug,非零值被吞 |
以及,一种内存安全问题:
如果没有 \0 结尾,但是指定精度,其实是可以的。
char s[] = {'A', 'B', 'C'};
snprintf(buf, sizeof(buf), "%.3s", s);当前的访问实现会越界读取,因为调用了 strnlen_s_来获取长度,但s可能根本不以 \0 结尾。glibc、musl等常见的libc是没问题的,嵌入式C库不确定,这个测试用例就不给三方库测了,就rtt自己加了测试用例。
测试用例
测试用例疑似有些场景被刻意绕过,例如下面的用例,显然是tiny版本实现错误,不应当与Stand版本区别对待。
SPRINTF_TEST_CASE(width)
{
char buffer[base_buffer_size];
// 为什么???
#ifdef RT_KLIBC_USING_VSNPRINTF_STANDARD
SPRINTF_CHECK("Hello testing", buffer, "%1s", "Hello testing");
#endif /* RT_KLIBC_USING_VSNPRINTF_STANDARD */
SPRINTF_CHECK("1024", buffer, "%1d", 1024);
}还有一些用例设计的不太好,RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS 宏是用于控制 %f和%F的,但里面包含了%s的用例,本来这些用例能够拦截tiny版本的实现问题,但这个宏仅在standard版本生效,因此用例被忽略,导致漏测:
SPRINTF_TEST_CASE(misc)
{
char buffer[base_buffer_size];
SPRINTF_CHECK("53000atest-20 bit", buffer, "%u%u%ctest%d %s", 5, 3000, 'a', -20, "bit");
#ifdef RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS
SPRINTF_CHECK("0.33", buffer, "%.*f", 2, 0.33333333);
SPRINTF_CHECK("1", buffer, "%.*d", -1, 1);
// 例如这个用例不应该放在宏里面
SPRINTF_CHECK("foo", buffer, "%.3s", "foobar");
SPRINTF_CHECK(" ", buffer, "% .0d", 0);
SPRINTF_CHECK(" 00004", buffer, "%10.5d", 4);
SPRINTF_CHECK("hi x", buffer, "%*sx", -3, "hi");
SPRINTF_CHECK("00123 ", buffer, "%-20.5i", 123);
SPRINTF_CHECK("-67224.546875000000000000", buffer, "%.18f", -67224.546875);
#endif /* RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS */
#ifdef RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
SPRINTF_CHECK("0.33", buffer, "%.*g", 2, 0.33333333);
SPRINTF_CHECK("3.33e-01", buffer, "%.*e", 2, 0.33333333);
SPRINTF_CHECK("0.000000e+00", buffer, "%e", 0.0);
// SPRINTF_CHECK("-0.000000e+00", buffer, "%e", -0.0);
#endif /* RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS */
}Other additional context
No response
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
KernelPR has src relate codePR has src relate codebugThis PR/issue is a bug in the current code.This PR/issue is a bug in the current code.in progressPR/issue in progress.PR/issue in progress.
Activity
github-actions commentedon Sep 24, 2026
如需认领并处理此 Issue,请评论
/claim。To claim and work on this issue, comment
/claim.luchenxu73 commentedon Sep 24, 2026
/claim
github-actions commentedon Sep 24, 2026
@luchenxu73 has claimed this issue.