Dan*_*aum 16 c++ stack-overflow printf buffer-overflow language-lawyer
今天,我从Elance.com上做了一个简短的"C++技能测试".一个问题如下:
以下代码行的安全漏洞是什么:
printf("%s", argv[1]);选项1:格式字符串
选项2:堆栈溢出 < - 这被Elance标记为正确答案
在最初几秒钟看到问题后(或自动使问题无效),用户被提供10秒钟来回答此问题.(还有另外两个明显不相关的答案,没有被Elance标记为正确答案.)
我正在寻找缓冲区溢出或缓冲区溢出作为选项.
我本能地不喜欢答案堆栈溢出,因为在我10秒内我精神上使用了我认为是"Stack Overflow"的标准定义:
在软件中,当堆栈指针超出堆栈限制时,会发生堆栈溢出.调用堆栈可能包含有限数量的地址空间,通常在程序开始时确定...
根据"Stack Overflow"的定义,在没有堆栈溢出的情况下完全可以实现缓冲区溢出 ; 只有当程序试图在调用程序的总堆栈分配之外写入时才会发生堆栈溢出(无论是由于缓冲区溢出,还是由于它是否是合法写入,例如为基于堆栈的变量分配内存过多而次).
我的10秒本能告诉我,"缓冲区溢出"是对上面有问题的代码行的更准确的描述 - 因为通常(根据我的经验),有足够的空字符('\0')通过RAM中的垃圾数据来填充,以避免在这种情况下实际的堆栈溢出,但实现中的缓冲区溢出似乎是合理可能的,甚至可能.(但是,这可能printf在这里读的垃圾可能会认为argc == 1,这样有是没有用户提供的argv[1];如果argv[1]存在,或许可以假设很可能是调用函数还没有插入NULL的这不是在问题陈述是否.argv[1]为当下.)
因为我想象这里可能存在缓冲区溢出问题,即使没有堆栈溢出,我回答了格式字符串,因为简单地通过传递不同的格式字符串"%.8s",问题可以大部分避免,所以它看起来像一个整体更通用,因此更好,回答.
我的回答被标记为错误.正确答案标记为"Stack Overflow".
现在我想到,如果假设argv[1]存在,那么唯一可能的缓冲区溢出是堆栈溢出,在这种情况下,堆栈溢出实际上可能是正确的答案.但是,即使在这种情况下,将这称为堆栈溢出也不会被认为是奇怪的吗?缓冲区溢出是否是描述此问题的更好方法,即使假设argv[1]存在?而且,如果argv[1]是不存在的,是不是非常不正确地指出,问题是堆栈溢出,而不是更准确的缓冲区溢出?
我想在这个网站上的专业人士的意见:"堆栈溢出"是用上述代码行定义内存安全问题的正确方法吗?或者说,"缓冲区溢出"或"缓冲区溢出"显然是描述问题的更好方法吗?最后,考虑到问题答案提供的两个选项(上图),答案是否含糊不清,或者"堆栈溢出"(或"格式字符串")显然是更好的答案?
有关Elance测试的切向评论 (与此帖子中的问题无关)
Elance" C++技能测试"问题都不涉及任何特定于C++的特性,例如类,模板,STL中的任何东西,或多态的任何方面.每个问题都是一个彻头彻尾的,直接来自C的问题.
因为有很多(至少3)Elance的所谓的"C++技能考试"那是无可争议的错误(比如这个问题其他问题:给定sizeof(int) == sizeof(int*)和sizeof(int) == 4,然后在代码int *a, *b; a=b; b++; b-a;是什么b-a,与列为正确的答案4,而比实际的正确答案1),并考虑到有没有C++的事实-在测试的具体问题,我所接触的Elance,并计划严肃追究他们的问题的测试与组织.但是,对于这篇文章中讨论的问题,我不确定问题/答案是否有问题.
这里没有潜在的堆栈溢出.
标准保证argc非负,这意味着它可以0.如果argc是肯定的,则argv[0]通过argv[argc-1]指向字符串的指针.
如果argc == 0,那么argv[1]它不仅仅是一个空指针 - 它根本不存在.在这种情况下,argv[1]尝试访问不存在的数组元素.(argv[1]相当于*(argv+1);允许指针添加,但取消引用具有未定义的行为.)请注意,在这种情况下,程序名称(否则可通过其访问argv[0])不可用.
如果argc==1,那么argv[1] == NULL.评估argv[1]完全有效,但它产生一个空指针.传递一个空指针printf与"%s"选项有不确定的行为.我想你可以称之为格式字符串问题,但真正的问题是当需要一个非字符串指向字符串时使用空指针.
如果argc >= 2,然后argv[1]保证指向一个字符串,printf("%s", argv[1])将简单地打印该字符串的字符,直到但不包括终止'\0'(保证存在).
在这种情况下仍然存在潜在的漏洞.引用N1570 7.21.6.1第15段:
任何单次转换可以产生的字符数应至少为4095.
(N1570是C标准的草案; C++是指其标准库部分的C标准.)
这意味着实现可能会限制printf调用产生的字符数.在实践中,可能没有理由施加固定限制; printf可以简单地一次打印一个字符,直到它到达字符串的末尾.但原则上,如果strlen(argv[1]) > 4095当前实现强加了这样的限制,那么行为可能是不确定的.
尽管如此,这并不是我称之为"堆栈溢出" - 特别是因为C++标准不使用"堆栈"这个词(除了对"堆栈展开"的几个简短引用).
通过先检查可以避免大多数这些问题:
if (argc >= 2) {
printf("%s", argv[1]);
}
Run Code Online (Sandbox Code Playgroud)
或者,如果你感到偏执:
if (argc >= 2 && argv[1] != NULL) {
printf("%s", argv[1]);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1217 次 |
| 最近记录: |