整数溢出时,(unsigned int) * (int) 的结果是无符号整数(unsigned int),而不是有符号整数(int)。

8

当整数溢出时,(unsigned int) * (int)的结果是什么?unsigned还是int? 对于char*,数组索引运算符(operator[])使用什么类型: intunsigned int或其他类型?

我在审计以下函数时,突然出现了这个问题。该函数在第17行存在漏洞。

// Create a character array and initialize it with init[] 
// repeatedly. The size of this character array is specified by 
// w*h.
char *function4(unsigned int w, unsigned int h, char *init)
{
    char *buf;
    int i;

    if (w*h > 4096)
        return (NULL);

    buf = (char *)malloc(4096+1);
    if (!buf)
        return (NULL);

    for (i=0; i<h; i++)
        memcpy(&buf[i*w], init, w);  // line 17

    buf[4096] = '\0';

    return buf;
}

考虑到 wh 都是非常大的无符号整数。第 9 行的乘法有通过验证的机会。
现在问题出在第 17 行。将 int iunsigned int w 相乘:如果结果是 int,则可能导致乘积为负数,从而访问了 buf 之前的位置。如果结果是 unsigned int,则乘积总是为正数,从而访问了 buf 之后的位置。
很难编写代码来证明这一点:int 太大了。有人有什么想法吗?
是否有任何规定产品类型的文档?我已经搜索过了,但迄今为止还没有找到任何东西。
我认为就漏洞而言,(unsigned int) * (int) 生成 unsigned intint 并不重要,因为在编译的目标文件中,它们只是字节。以下代码对于产品类型的类型都是相同的:
unsigned int x = 10;
int y = -10;

printf("%d\n", x * y);  // print x * y in signed integer
printf("%u\n", x * y);  // print x * y in unsigned integer

因此,乘法返回的类型并不重要。重要的是消费函数是否使用intunsigned
这里的问题不是函数有多差,或者如何改进函数使其更好。函数无疑存在漏洞。问题是基于标准规定的预期行为,该函数的确切行为是什么。
13个回答

0

memcpy(&buf[iw > -1 ? iw < 4097? iw : 0 : 0], init, w); 我认为iw的三次计算并不会降低性能)


0

无符号算术运算是作为模数(或环绕)进行的,因此两个大的无符号整数的乘积很容易小于4096。int和unsigned int的乘法将导致一个unsigned int(请参见C++标准的第4.5节)。

因此,给定大的w和适当的h值,您确实可能会遇到麻烦。

确保整数算术不会溢出很困难。一种简单的方法是转换为浮点数并进行浮点乘法,然后查看结果是否合理。正如qwerty建议的那样,如果在您的实现中可用,则可以使用long long。(它是C90和C++中的常见扩展名,在C99中存在,并且将在C++0x中使用。)


你能告诉我在哪里可以找到C++标准的在线副本吗? - yinyueyouge
我从http://webstore.ansi.org/上获取了它 - 搜索C++标准。它的价格是30美元。 ANSI和其他标准化组织通常通过出售其标准副本来为自己筹集资金。 - David Thornley
荒谬的建议-转换为浮点数?!尝试使用if (!h || w> 4096 / h) return NULL; - R.. GitHub STOP HELPING ICE

-1
实际上回答你的问题,如果没有指定你运行的硬件,你是不知道的,在旨在可移植的代码中,你不应该依赖于任何特定的行为。

1
无符号算术在C和C++中有定义。给定最大的无符号整数值(可以在某个头文件中找到),您可以精确地预测会发生什么。 - David Thornley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接