为什么在填充数组时 scanf() 不会产生内存破坏错误. [英] Why doesn't scanf() generate memory clobbering errors when filling array.

查看:36
本文介绍了为什么在填充数组时 scanf() 不会产生内存破坏错误.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定一个有 5 个元素的数组,众所周知,如果你使用 scanf() 读入正好 5 个元素,那么 scanf() 将填充该数组,然后通过将一个空字符 '\0' 放入来破坏内存没有产生错误的第 6 个元素(我称它为第 6 个元素,但我知道它的内存不是数组的一部分)如下所述:字符数组的空终止

Given an array with 5 elements, it is well known that if you use scanf() to read in exactly 5 elements, then scanf() will fill the array and then clobber memory by putting a null character '\0' into the 6th element without generating an error(Im calling it a 6th element but I know its memory thats not part of the array) As is described here: Null termination of char array

但是,当您尝试读取 6 个或更多元素时,会生成错误,因为操作系统检测到内存正在被破坏并且内核发送信号.有人能解释一下为什么在上面的第一种内存破坏情况下没有产生错误吗?

However when you try to read in 6 elements or more an error is generated because the OS detects that memory is being clobbered and the kernel sends a signal. Can someone clear up why an error is not generated in the first case of memory clobbering above?

示例代码:

// ex1.c
#include <stdio.h>
int main(void){
  char arr[5];
  scanf("%s", arr);
  printf("%s\n", arr);
  return 0;
}

编译、运行并输入四个字符:1234.这将它们正确地存储在数组中并且不会破坏内存.这里没有错误.

Compile, run and enter four characters: 1234. This stores them in the array correctly and doesn't clobber memory. No error here.

$ ./ex1
1234
1234

再次运行并输入五个字符.这将破坏内存,因为 scanf() 在内存中的第 5 个元素之后存储了一个额外的 '\0' 空字符.不会产生错误.

Run again and enter five characters. This will clobber memory because scanf() stored an extra '\0' null character in memory after the 5th element. No error is generated.

$ ./ex1
12345
12345

现在输入我们希望破坏记忆的六个字符.生成的错误看起来像(即我猜)它是内核发送的信号的结果,说我们只是以某种方式破坏了堆栈(本地内存)......为什么会为此内存破坏生成错误,但是不是上面的前一个吗?

Now enter six characters which we expect to clobber memory. The error that is generated looks like(ie. Im guessing) its the result of a signal sent by the kernel saying that we just clobbered the stack(local memory) somehow....Why is an error being generated for this memory clobbering but not for the previous one above?

$ ./ex1
123456
123456
*** stack smashing detected ***: ./ex1 terminated
Aborted (core dumped)

无论我制作的数组大小如何,这似乎都会发生.

This seems to happen no matter what size I make the array.

推荐答案

行为是 undefined 如果在这两种情况下您输入的字符数都超过了缓冲区可以容纳的字符数.

The behaviour is undefined if in both the cases where you input more than characters than the buffer can hold.

堆栈粉碎检测机制使用 canaries.当 Canary 值被覆盖时,会生成 SIGABRT.之所以没有生成,可能是因为数组后面至少有一个额外的内存字节(通常需要对象的最后一个字节是有效指针.但它不能被使用存储到值 - 合法).从本质上讲,当您输入 1 个额外字符时,金丝雀不会被覆盖,但是当您由于某种原因输入 2 个字节时,它会被覆盖,从而触发 SIGABRT.

The stack smashing detection mechanism works by using canaries. When the canary value gets overwritten SIGABRT is generated. The reason why it doesn't get generated is probably because there's at least one extra byte of memory after the array (typically one-past-the-end of an object is required to be a valid pointer. But it can't be used to store to values -- legally). In essence, the canary wasn't overwritten when you input 1 extra char but it does get overwritten when you input 2 bytes for one reason or another, triggering SIGABRT.

如果在arr之后还有一些其他的变量,例如:

If you have some other variables after arr such as:

#include <stdio.h>
int main(void){
  char arr[5];
  char var[128];
  scanf("%s", arr);
  printf("%s\n", arr);
  return 0;
}

然后当您输入更多字节时,金丝雀可能不会被覆盖,因为它可能只是覆盖了 var.从而延长编译器对缓冲区溢出的检测.这是一个合理的解释.但无论如何,如果您的程序超出缓冲区,则您的程序无效,您不应该依赖编译器的堆栈粉碎检测来拯救您.

Then the canary may not be overwritten when you input few more bytes as it might be simply overwriting var. Thus prolonging the buffer overflow detection by the compiler. This is a plausible explanation. But in any case, your program is invalid if it overruns buffer and you should not rely the stack smashing detection by the compiler to save you.

这篇关于为什么在填充数组时 scanf() 不会产生内存破坏错误.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆