C++ 代码行的内存漏洞是否应为“;printf(“%s”,argv[1])&引用;是否被描述为堆栈溢出?

C++ 代码行的内存漏洞是否应为“;printf(“%s”,argv[1])&引用;是否被描述为堆栈溢出?,c++,printf,stack-overflow,language-lawyer,buffer-overflow,C++,Printf,Stack Overflow,Language Lawyer,Buffer Overflow,今天,我在Elance.com上做了一个简短的“C++技能测试”。一个问题是: 以下代码行的安全漏洞是什么: printf(“%s”,argv[1]) 选项1:格式化字符串 选项2:Unix系统上的堆栈溢出argv[1]本身的无效内存访问(大小写argc==0),指向格式正确的字符串(argc>=2)的指针,或NULL(argc==1) printf(“%s”,argv[1])的问题正在使用指针(argv[1]),但未检查其是否有效。以后发生的任何事情都只是次要后果。问题是在使用前未能验证arg

今天,我在Elance.com上做了一个简短的“C++技能测试”。一个问题是:

以下代码行的安全漏洞是什么:

printf(“%s”,argv[1])

选项1:格式化字符串


选项2:Unix系统上的堆栈溢出
argv[1]
本身的无效内存访问(大小写
argc==0
),指向格式正确的字符串(
argc>=2
)的指针,或NULL(
argc==1

printf(“%s”,argv[1])的问题
正在使用指针(
argv[1]
),但未检查其是否有效。以后发生的任何事情都只是次要后果。问题是在使用前未能验证
argv[1]
是否符合预期。它可能属于非常普遍的范畴。将其称为缓冲区溢出或堆栈溢出会产生误导。

C++标准响应 就语言而言,可能存在以下情况:

  • argc<2
  • argc>=2
  • 在第一种情况下,
    printf(“%s”,argv[1])
    只是未定义的行为

    在第二种情况下,程序格式良好(从
    argv[0]
    argv[argc-1]
    保证为有效的空终止字符串:

    §3.6.1/2[basic.start.main]

    在后一种形式中,为了便于说明,第一个函数参数称为argc,第二个函数参数称为argv,其中argc应为从运行程序的环境传递给程序的参数数。如果argc为非零,则应通过argv在argv[0]中提供这些参数[argc-1]作为指向以空结尾的多字节字符串(ntmbs)(17.5.2.1.4.2)的初始字符的指针,argv[0]应是指向ntmbs的初始字符的指针,该字符表示用于调用程序或“”的名称。argc的值应为非负。argv[argc]的值应为0。[注:建议添加任何其他(可选)参数Terargv.-尾注]

    (我的重点)

    为什么堆栈溢出非常不精确

    由于没有其他信息(作为编译器或体系结构),回答“堆栈溢出”是不精确的。C++标准不试图定义“栈”是什么,因此“堆栈溢出”几乎意味着没有什么C++标准。 标准的理由是一个具有保证内存模型的抽象机器

    到底发生了什么
    argc<2
    的情况下,没有人知道会发生什么。标准既不保证也不指定任何内容。在
    argc>=2
    的情况下,程序定义良好。

    这里没有潜在的堆栈溢出

    该标准保证
    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'
    (保证存在)

    在这种情况下仍然存在潜在的漏洞。引用7.21.6.1第15段:

    任何一次转换可产生的字符数应至少为 4095

    <15>(N1570是C标准的草案;C++是指标准库的部分的C标准)。 这意味着一个实现可能会限制
    printf
    调用产生的字符数。实际上,可能没有理由施加固定的限制;
    printf
    可以简单地一次打印一个字符,直到它到达字符串的末尾。但原则上,如果
    strlen(argv[1])>4095
    ,如果当前的实现施加了这样的限制,那么行为可能是未定义的

    不过,这并不是我所说的“堆栈溢出”——特别是因为C++标准不使用“栈”这个词(除了一些简短的引用“堆栈展开”)之外。 通过首先检查,可以避免这些问题中的大多数:

    if (argc >= 2) {
        printf("%s", argv[1]);
    }
    
    或者,如果你感到偏执:

    if (argc >= 2 && argv[1] != NULL) {
        printf("%s", argv[1]);
    }
    

    @self.绝对没有。在Elance测试中,用户只收到了一行代码,与我在本文中提供的完全相同-问题中没有提供任何上下文,只有一个问题,即以下代码行的安全漏洞是什么,提供了四个多选选项。(在“C++技能测试”中,这是引导。)那些“不相关的答案”是什么?它们实际上高度相关吗?
    argv[1]
    可以是任何东西。可能没有任何参数传递给程序,在这种情况下,这指向