使用%d时,fscanf缓冲区是否会溢出?

clj7thdc  于 2023-06-28  发布在  其他
关注(0)|答案(3)|浏览(170)

我在ossec-hids repo上运行Fortify Static Code Analyzer,它报告了src/analysisd/stats.c:415的以下Buffer Overflow: Format String结果:
stats.c第415行的fscanf()的格式字符串参数没有正确限制函数可以写入的数据量,这允许程序在分配的内存范围之外写入。此行为可能会损坏数据、使程序崩溃或导致执行恶意代码
有问题的代码行是这样的:

if (fscanf(fp, "%d", &_RWHour[i][j]) <= 0) {

_RWHour声明为

static int _RWHour[7][25];

在同一文件的第33行。我相信在第33行的声明和第415行的使用之间没有_RWHour的影子,因为当我选择第33行的声明时,我的IDE(Visual Studio 2019)在415中突出显示_RWHour
当我看cppreference documentation for fscanf时,它说:

d匹配十进制整数。数字的格式与strtol所期望的格式相同,其基本参数的值为10

上面引用的表格还显示,当%d没有使用长度修饰符时(就像正在讨论的fscanf调用一样),参数类型应该是signed int*unsigned int*
我的问题是:
在这种情况下,Fortify发现是否可能是假阳性?或者,当您将int的地址传递给fscanf时,是否可以写入int外部的内存?
如果将%dfscanf一起使用时,可能会在int的内存之外写入,如何安全地避免这种情况?

5uzkadbs

5uzkadbs1#

防止转换溢出

fscanf(fp, "%d", &_RWHour[i][j])undefined behavior(UB)*1,如果数字文本试图转换为int范围之外的值。
防止UB的一个快速修复方法是使用 width 限制读取的字符数:

//fscanf(fp, "%d", &_RWHour[i][j])
fscanf(fp, "%4d", &_RWHour[i][j])  // Limit [-999 ... 9999].

一个更健壮的解决方案是读入文本并使用strtol()进行转换。
我建议创建一个helper函数来处理int中的阅读。
这种程度的检查有点臭,不是吗?

i, j在范围内

对OP的引用代码的检查看起来没问题,但分析工具可能会抱怨。

  • 1

...或者如果转换的结果无法在对象中表示,则行为未定义。C23dr § 7.23.6.2
有了UB,“可以在int之外写入内存”。

ua4mk5z4

ua4mk5z42#

Fortify的发现是否可能是假阳性?
当然,一般来说,Fortify报告的是假阳性。尽管@chux关于可能的未定义行为的回答,我认为这确实是一个错误的肯定。至少,这是一个不准确的诊断。从形式上讲,一个执行未定义行为的程序可以做任何事情,但在实践中,在这种情况下,超出指定int对象的界限是极不可能的结果,并且诊断会忽略所有其他可能的结果。
假设其他参数在格式方面正确匹配,scanf家族函数溢出目标对象的主要风险是没有宽度的%s%[转换规范,或者宽度太大的%s%[%c转换规范。我认为Fortify是不恰当的概括。
或者,当你把int的地址传递给fscanf时,是否有可能在int之外写入内存?
如果不涉及未定义的行为,无论是输入过长还是来自fscanf()调用之外的其他源,都是如此。
如果在fscanf中使用%d时,可能会在int的内存之外写入,那么如何安全地避免这种情况?
实际上,我不认为这是值得关注的事情。
尽管如此,您可能可以通过向转换说明符添加适当的字段宽度来满足Fortify的要求,尽管在这种情况下确定“适当”的含义可能很棘手。这也将排除任何可能的UB所产生的呼吁,这是一个有价值的目标。
或者,您可以通过fscanf()以外的其他方式解析数字。

py49o6xq

py49o6xq3#

代码似乎不保证该工具报告的问题。
此外,在同一个文件中还有另一个对fscanf的调用,它应该报告相同的问题,除了目标int是1D数组而不是2D数组中的条目:

381                if (fscanf(fp, "%d", &_RHour[i]) <= 0) {

但在这两种情况下,很明显,工具应该推断出索引变量保持在适当的边界内。
您可以尝试使用以下代码更改第404..426行中的循环,以调查是否是工具造成的:

for (j = 0; j <= 24; j++) {
            _CWHour[i][j] = 0;
            _RWHour[i][j] = 0;
            snprintf(_weekly, 128, "%s/%d/%d", STATWQUEUE, i, j);
            if (File_DateofChange(_weekly) >= 0) {
                FILE *fp = fopen(_weekly, "r");
                if (fp != NULL) {
                    int hour;
                    if (fscanf(fp, "%d", &hour) == 1 && hour >= 0) {
                        _RWHour[i][j] = hour;
                    }
                    fclose(fp);
                }
            }
        }

相关问题