#clock #vdso #time #testing #range #trampoline #writable

bin+lib tpom

允许覆盖系统时钟;对于测试很有用

1 个不稳定版本

0.1.0 2022年12月2日

#8 in #writable

MIT 许可证

21KB
266

TPOM


此库劫持了 vDSO(虚拟动态共享对象)中的时间相关函数,并允许用用户提供的函数替换它们。

例如,嵌入到一个 Python 项目中

>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2022, 12, 1, 13, 47, 15, 574866)
>>> import tpom
>>> tpom.curse_me()
>>> datetime.datetime.now()
my clockgettime was called!
datetime.datetime(1970, 1, 1, 1, 1, 51)

libfaketime 启发,主要区别是不需要 LD_PRELOAD 任何共享对象(并在之后重新执行)。

工作原理

为了加快频繁调用的系统调用,内核通过 vDSO 暴露它们,这是一种将内核函数映射到用户内存空间的方法。
vDSO 内存区域包含一个 ELF 块,可以扫描动态符号,例如

DYNAMIC SYMBOL TABLE:
0000000000000c10  w   DF .text  0000000000000005  LINUX_2.6   clock_gettime
0000000000000bd0 g    DF .text  0000000000000005  LINUX_2.6   __vdso_gettimeofday
0000000000000c20  w   DF .text  0000000000000060  LINUX_2.6   clock_getres
0000000000000c20 g    DF .text  0000000000000060  LINUX_2.6   __vdso_clock_getres
0000000000000bd0  w   DF .text  0000000000000005  LINUX_2.6   gettimeofday
0000000000000be0 g    DF .text  0000000000000029  LINUX_2.6   __vdso_time
0000000000000be0  w   DF .text  0000000000000029  LINUX_2.6   time
0000000000000c10 g    DF .text  0000000000000005  LINUX_2.6   __vdso_clock_gettime

这些符号中的每一个都是一个可以调用的函数。

这是 用户空间 内存,每个进程都有 完全 控制权,这就是 TPOM 所做的

  • 通过扫描 /proc/self/maps 找到 vDSO 内存范围
  • 读取 vDSO ELF 块
  • 使 vDSO 范围可写
  • 在符号的地址上写入一个 jmp 作为第一条指令,指向最终调用用户提供的函数的 跳板

vDSO 之前

0000000000000c10 <__vdso_clock_gettime@@LINUX_2.6>:
 c10:	e9 9b fb ff ff       	jmp    7b0 <LINUX_2.6@@LINUX_2.6+0x7b0>
 c15:	66 66 2e 0f 1f 84 00 	data16 cs nop WORD PTR [rax+rax*1+0x0]
 c1c:	00 00 00 00 

之后

0000000000000c10 <__vdso_clock_gettime@@LINUX_2.6>:
 c10:	48 b8 50 5b 7e 2c 37 	movabs rax,0x56372c7e5b50
 c17:	56 00 00 
 c1a:	ff e0                	jmp    rax
 c1c:	90                   	nop
 c1d:	90                   	nop
 c1e:	90                   	nop
 c1f:	90                   	nop

注意

  • 如果你的代码直接执行系统调用,则 此方法将不起作用
  • 仅适用于 x86_64,在 Linux 上。
  • 不需要 LD_PRELOAD

依赖关系

~1MB
~18K SLoC