Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
scanf溢出(“8s”,字符串)?_C_Scanf_Buffer Overflow - Fatal编程技术网

scanf溢出(“8s”,字符串)?

scanf溢出(“8s”,字符串)?,c,scanf,buffer-overflow,C,Scanf,Buffer Overflow,我知道普通代码可能会溢出: 字符串[9] scanf(“%s”,字符串) 但是有可能溢出scanf(“%8s”,字符串)吗?8只是一个例子 我知道“%8s”的工作原理类似于定界,但我也注意到,当我输入长度超过8个字符的字符串时,程序将终止,原因是: *检测到堆栈崩溃*:./a.out终止 =========回溯:========= 显然有一个标志检测默认情况下由GCC打开的堆栈破坏。由于这是一个堆栈崩溃,那么我的猜测是仍然有可能溢出并执行任意代码 与破坏scanf(“%s”)调用方的正常溢出相

我知道普通代码可能会溢出:

字符串[9]

scanf(“%s”,字符串)

但是有可能溢出scanf(“%8s”,字符串)吗?8只是一个例子

我知道“%8s”的工作原理类似于定界,但我也注意到,当我输入长度超过8个字符的字符串时,程序将终止,原因是:

*检测到堆栈崩溃*:./a.out终止

=========回溯:=========

显然有一个标志检测默认情况下由GCC打开的堆栈破坏。由于这是一个堆栈崩溃,那么我的猜测是仍然有可能溢出并执行任意代码

与破坏scanf(“%s”)调用方的正常溢出相反,如果scanf(“%8s”)可以溢出,则它将在scanf函数中溢出,以便在scanf尝试返回时获得控制权

但是scanf是一个需要模式切换(从用户模式切换到内核模式)的系统调用,在内部它将调用诸如读取stdin等内容。因此不确定我们是否可以在内核模式或其他模式下溢出

欢迎评论

更新>>

在上面的示例中,假定为字符字符串[9]。以下实数代码中的字符字符串[8]

问题实际上是关于安全scanf(“%8s”)和GCC之间由于堆栈崩溃而中止的看似矛盾的故事

简化代码:

void foo(pass some pointer) {
char input[8];
int input_number = 0;

while (1) { // looping console
   printf some info;
   scanf("%8s", input);

   input_number = atoi(input);

   if ((strlen(input) == 1) && (strncmp(input, "q", 1) == 0)) {
       input_number = -1;
   }
   switch (input_number) {
       case -1: to quit the console if input = 'q';
       default: to print info that pointer refers to;
       ...
   } 

}

}
注:

  • foo被其他人调用
  • 虽然字符串是8个字节的实数 用“%8s”编码,我不这么认为 导致粉碎

  • 如果字符串分配少于8个特许,它肯定会覆盖缓冲区,而且scanf不会附加空终止符。但只要字符串中有足够的空间容纳值,就不应该得到正确的值

    如果你想让你的输入更加稳健,千万不要使用
    scanf
    (或者
    fscanf

    您应该使用
    fgets
    (或类似的“防止缓冲区溢出”变体),然后在此基础上使用
    sscanf

    scanf
    fscanf
    的主要问题是,如果行不是预期的格式(即,如果
    scanf
    失败),文件指针可能会位于不确定的位置。使用
    fgets/sscanf
    方法,可以更轻松地保证您处于行边界上,而不必使用
    ftell
    fseek
    来移动文件

    关于您关于缓冲区是否会溢出的特定查询,C标准有如下说明:

    。。。相应的参数应该是一个指向字符数组初始元素的指针,该数组足够大,可以接受序列和一个将自动添加的终止空字符

    因此,对于
    “%8s”
    格式,您需要一个9个字符的数组

    我怀疑你的代码中还有其他问题。使用测试程序:

    #include <stdio.h>
    int main(int argc, char* argv[]) {
        char x1;
        char a[9];
        char x2;
        x1 = x2 = ' ';
        scanf ("%s",a);
        printf ("[%c] [%s] [%c]\n",x1,a,x2);
        return 0;
    }
    
    当我将同一程序更改为使用
    “%8s”
    时,我得到(对于完全相同的输入):

    见:

    每个指令由以下内容之一组成…一个可选的非零十进制整数,用于指定最大字段宽度

    s
    匹配非空白字符的字节序列。应用程序应确保相应的参数是指向字符、有符号字符或无符号字符数组的初始字节的指针,该数组的大小足以接受序列和一个自动添加的终止空字符代码


    因此它不会使9字节字符串缓冲区溢出。

    正如ysth所指出的,数组应该能够包含字符串和终止的空字符,因此使用8字节数组(特别是如果它是在堆栈上分配的,就像在代码中一样)很可能会把它搞砸。

    至少需要9个字节才能不溢出。事实上,我认为scanf会在末尾加一个“\0”。C标准表示“一个终止的空字符,它将被自动添加。”paxdiabloI也引用了这句话,意思是如果你没有空字符的空间。如果你没有空字符的空间,它将把它放在那里(如果必要,覆盖堆栈的其他部分).scanf是一个运行时库函数,由于它在用户空间中运行,因此不需要模式切换,除非它必须请求缓冲区填充,在这种情况下,它将调用read或fread。如答案中多次指出的,添加了nul字节,所以你需要一个9个字符的缓冲区来接受最多8个字符的输入。正如很多人指出的,你在“注2”中的假设是错误的。这个例子允许一个字节的溢出,这是gcc正在检测的。你们说得对。我已经用一个更简单的程序测试了它,但上次我试的时候它没有崩溃。现在,当我为字符串[8]和scanf(%8s)输入“12345678”时,由于堆栈崩溃,它崩溃了!这是我们学到的教训。粉碎并不一定意味着存在堆栈溢出攻击。在这种情况下,即使缓冲区恰好位于堆栈上,编程错误也是缓冲区溢出,而不是堆栈溢出。我相应地重新标记了问题。是的,我知道。但是现在我很想知道scanf(%8s)是否和scanf有同样的问题,因为GCC告诉我仍然有堆栈崩溃发生!描述“健壮”?举几个例子?@ysth:(1)将您的输入作为行。(2) 确保获得整行(\n字符在末尾),否则错误为“行太长”。(3) 在线使用sscanf-您可以在在线上任意多次执行此操作,而无需担心底层文件。我也像您一样测试了类似的简化代码,GCC不会抱怨堆栈崩溃。。我已经发布了orig代码,但在运行时,当我输入12345678(注意实码字符字符串[8])时,会出现GCC抱怨。@Figo,您需要一个9字符数组来存储一个8字符字符串。这是因为一个8个字符的字符串由8个字符组成(显而易见)
    pax> ./qq.exe
    dfjdhadgha...lghjdfgjhd
    [s] [dfjdhadgha...lghjdfgjhd] [ ]
      6 [main] qq 4744 _cygtls::handle_exceptions: Error while dumping state
      (probably corrupted stack)
      Segmentation fault (core dumped)
    
    pax> ./qq.exe
    dfjdhadgha...lghjdfgjhd
    [ ] [dfjdhadg] [ ]