Skip to content

[Bug] wrong impl of vsnprintf #11833

Open

Description

@luchenxu73

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

Activity

  1. added
    bugThis PR/issue is a bug in the current code.
    on Sep 24, 2026
  2. github-actions commented on Sep 24, 2026

    @github-actions

    如需认领并处理此 Issue,请评论 /claim。

    To claim and work on this issue, comment /claim.

  3. luchenxu73 commented on Sep 24, 2026

    @luchenxu73
    ContributorAuthor

    /claim

  4. github-actions commented on Sep 24, 2026

    @github-actions

    @luchenxu73 has claimed this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

KernelPR has src relate codebugThis PR/issue is a bug in the current code.in progressPR/issue in progress.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions