如果攻击者控制您的输入,这两种变体都不安全
该表达式p1 + len < p2
编译为类似p1 + sizeof(*p1)*len < p2
,并且使用指向类型的大小进行缩放可能会溢出您的指针:
int *p1 = (int*)0xc0ffeec0ffee0000;
int *p2 = (int*)0xc0ffeec0ffee0400;
int len = 0x4000000000000000;
if(p1 + len < p2) {
printf("pwnd!\n");
}
当len
乘以 的大小时int
,它会溢出到,0
因此条件被评估为if(p1 + 0 < p2)
。这显然是正确的,并且以下代码以过高的长度值执行。
好的,那怎么办p2-p1 < len
。同样的事情,溢出会杀死你:
char *p1 = (char*)0xa123456789012345;
char *p2 = (char*)0x0123456789012345;
int len = 1;
if(p2-p1 < len) {
printf("pwnd!\n");
}
在这种情况下,指针之间的差异被评估为p2-p1 = 0xa000000000000000
,这被解释为负符号值。因此,它比较小 then len
,并且以下代码以太低的len
值(或太大的指针差异)执行。
我知道在存在攻击者控制的值的情况下唯一安全的方法是使用无符号算术:
if(p1 < p2 &&
((uintptr_t)p2 - (uintptr_t)p1)/sizeof(*p1) < (uintptr_t)len
) {
printf("safe\n");
}
不能产生真正负值的p1 < p2
保证。p2 - p1
第二个子句执行p2 - p1 < len
while 以非 UB 方式强制使用无符号算术的操作。Ie准确给出了 large和(uintptr_t)p2 - (uintptr_t)p1
small 之间的字节数,无论涉及的值如何。p2
p1
当然,您不希望在代码中看到此类比较,除非您知道需要防御坚定的攻击者。不幸的是,这是确保安全的唯一方法,如果您依赖问题中给出的任何一种形式,您就会面临攻击。