[Bug] [libc] timer_gettime 拆秒前将总纳秒截断为 uint32_t,剩余时间查询错误
维护者通常 1 天内回复
@Yunlk 已经在做这个了。
开始于 2026年10月24日。
评估
- 难度
- 2/5
- 预计耗时
- 1-3 小时
- 新手友好度
- 68/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- c
调研方向
阅读 components/libc/compilers/common/ctime.c,特别是报告行号 1337–1375 附近的 timer_gettime(),并跟踪计数器剩余 tick 如何转换为纳秒。独立的算术示例展示了截断;请复现该示例,然后验证更改后剩余时间为 10 秒时的结果。保留对计数器回绕和中间乘法范围的检查,并确认 POSIX 定时器配置会启用此代码。
由索引模型根据 Issue 内容生成。
描述
RT-Thread Version
master, commit a59e72fb5c2f272761a2813fc9bd9fe18eb29d7d.
Affected area
libc / POSIX timers, components/libc/compilers/common/ctime.c, timer_gettime().
Hardware / Architecture / Toolchain
采用源码级算术隔离复现,不依赖特定开发板。该提交中的受影响实现由 RT_USING_POSIX_TIMER && RT_USING_CLOCK_TIME 启用。本报告尚未进行目标板复现。
问题描述 / Describe the bug
timer_gettime() 将总剩余纳秒存入 rt_uint32_t,之后才拆分秒和纳秒:
rt_uint32_t seconds, nanoseconds;
/* ... */
nanoseconds = ((remain_cnt - rt_clock_time_get_counter())
* rt_clock_time_get_res_scaled()) / RT_CLOCK_TIME_RESMUL;
seconds = nanoseconds / NANOSECOND_PER_SECOND;
nanoseconds = nanoseconds % NANOSECOND_PER_SECOND;
即使计数器表达式正确算出 10 秒,即 10,000,000,000 ns,赋给 32 位无符号变量时也会变成 1,410,065,408 ns。查询结果因此是 {tv_sec=1, tv_nsec=410065408},而不是 {10, 0}。
可运行的隔离复现 / Standalone arithmetic reproduction
下面明确模拟计数器换算已经正确得到总纳秒的情况,不调用宿主 libc 的 timer_gettime():
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
int main(void)
{
uint64_t converted_ns = UINT64_C(10000000000);
uint32_t seconds, nanoseconds;
nanoseconds = converted_ns;
seconds = nanoseconds / 1000000000;
nanoseconds %= 1000000000;
printf("%u.%09u\n", (unsigned)seconds, (unsigned)nanoseconds);
assert(seconds == 1 && nanoseconds == 410065408);
return 0;
}
输出:1.410065408;预期剩余时间为 10.000000000。
此前还在本地旧版同一函数的隔离计数器桩中复现过该结果;最新上游同样保留上述窄化顺序。完整目标端验证仍待进行。
Scope / Suggested direction
- 问题从总纳秒超过
UINT32_MAX(约 4.295 秒)起出现,不是几十年后的时间范围问题。 - 这是剩余时间查询结果错误,不表示定时器一定提前或延后触发。
- 后面的
(rt_int32_t)seconds不是这里的根因,数据在拆秒之前已经丢失。 - 建议总纳秒使用足够宽的类型,再拆分秒和纳秒;或在安全的单位下先拆秒、再换算余数。还应保留对计数器回绕及中间乘法范围的检查。
引入提交和版本 / Regression history
- 引入提交:0eb75ced70,2023-07-17,
[time]时钟框架重构 (#7794);首个包含该提交的正式 release 为 v5.0.2。 - 此前使用 64 位
remaining保存总纳秒,再分别赋值给秒和纳秒字段;该提交改为将总纳秒直接赋给 32 位nanoseconds,然后再拆秒。 rt_uint32_t seconds, nanoseconds的声明本身在更早实现中已经存在,但当时用于保存拆分后的字段,并非这次回归的来源。- 后续 743b614875 的时间子系统重构更换了计数接口名称,但保留了该窄化顺序。
其他实现参考 / Comparison
Linux 使用 64 位 ktime_t 转 timespec64;Zephyr 则先从剩余毫秒中拆秒,再将余数转换为纳秒。两种方式均避免这里的总纳秒 32 位中转。这不是对这些 OS 所有计时边界行为的保证。
- 主要语言
- C
- 星标
- 12.3k
- 派生
- 5.5k
- 平均合并
- 4 天 9 小时
- 30 天内合并 PR
- 38
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
RT-Thread/rt-thread 的其他 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 67/100
RT-Thread/rt-thread#11951 · 1 条评论 ·
维护者通常 1 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 72/100
RT-Thread/rt-thread#11949 · 1 条评论 ·
维护者通常 1 天内回复
-
[Bug] RT-Thread AB32 ADC uses an unchecked channel as a register index可能已有人在做 @usualtreee 于 3 天前认领。 未关闭bug Component component: drivers
难度 2/5 1-3 小时 新手友好度 72/100
RT-Thread/rt-thread#11948 · 1 条评论 ·
维护者通常 1 天内回复
-
[Bug] dfs_v2 tmpfs: ftruncate(fd, 0) frees the file buffer but reports success — use-after-free read/write and double free from a legal POSIX call可能已有人在做 @remukisama 于 14 天前认领。 未关闭in progress
难度 2/5 1-3 小时 新手友好度 78/100
RT-Thread/rt-thread#11902 · 4 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
[bsp][stm32][bluepill] README「快速上手」缺少重新生成 MDK 工程这一步,按文档操作无法编译通过可能重新可做 @moment-NEW 于 32 天前认领,目前没有进行中的 PR。 未关闭in progress
难度 2/5 1-3 小时 新手友好度 75/100
RT-Thread/rt-thread#11818 · 4 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
查看 RT-Thread/rt-thread 的全部 Issue
相似的 Issue
-
Bug
难度 2/5 1-3 小时 新手友好度 66/100
维护者通常 1 天内回复
-
Integer overflow → heap out-of-bounds write in `contrib/pngminus/pnm2png.c` via crafted PNM width未关闭
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 4 天内回复
-
难度 2/5 1-3 小时 新手友好度 70/100
meshcore-dev/MeshCore#3760 · 1 条评论 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 68/100
ly05010419/MacNativeGames#1697 ·
维护者通常 1 天内回复
-
severity: low
难度 2/5 1-3 小时 新手友好度 72/100
luainkernel/lunatik#2318 ·
维护者通常 1 天内回复