为什么此代码中的缓冲区溢出行为与我的预期不同?

2024-01-01

我有这个程序:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void main(void) {
    char *buffer1 = malloc(sizeof(char));
    char *buffer2 = malloc(sizeof(char));

    strcpy(buffer2, "AA");

    printf("before: buffer1 %s\n", buffer1);
    printf("before: buffer2 %s\n", buffer2);

    printf("address, buffer1 %p\n", &buffer1);
    printf("address, buffer2 %p\n", &buffer2);

    strcpy(buffer1, "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");

    printf("after: buffer1 %s\n", buffer1);
    printf("after: buffer2 %s\n", buffer2);
}

哪个打印:

before: buffer1 
before: buffer2 AA
address, buffer1 0x7ffc700460d8
address, buffer2 0x7ffc700460d0
after: buffer1 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
after: buffer2 B

我期望这段代码做什么:

  • 由于 char 的长度为 8 位,因此我希望两个缓冲区的大小均为 1 字节/8 位。

  • 一个 ASCII 字符有 7 位长,我希望每个缓冲区能容纳两个字符。

  • 当我直接分配两个一个字节的缓冲区时,我希望它们在内存中直接相邻。因此,我预计每个地址之间的差异是 1(因为内存是按字节寻址的?),而不是我的小程序打印的 8。

  • 由于它们在内存中直接相邻,我预计缓冲区 2 会溢出BB当我做strcpy(buffer1, BBBB);作为第一个BB写给buffer1其余的溢出到buffer2。因此,我希望strcpy(buffer1, "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");产生:

    • 缓冲区溢出buffer2,使其具有值BBBBBBBBBBBBBBBBBBBBBBBBBBBBB or so.

      • 我是如何计算的:金额B已被 strcpy'd - 4B两个缓冲区。
    • 分段错误。我只分配了 2 个字节(因为大小buffer1 and buffer2总共 2 个字节)。自从BBBBBBBBBBBBBBBBBBBBBBBBB两者都不适合buffer1 nor buffer2(因为两者都已经被填满),之后将溢出到下一个内存缓冲区buffer2。因为我没有分配它,所以我预计会出现分段错误。

因此,我想问:为什么我的程序表现与我的预期不同?我哪里误解了事情?

我有一个 x86_64 架构,上面的程序是用gcc version 6.3.1 20170306 (GCC)

我不要求的:

  • 我知道strcpy不是绑定检查并且使用是有意的。我想调查缓冲区溢出等问题。因此,请不要写答案/评论说我应该使用不同的方法strcpy.

  • 由于 char 是 8 位长,...

这对于所述架构和操作系统来说是正确的。 (C 标准允许char to be more超过8位长,但现在这种情况很少见;我知道的唯一例子是TMS320 https://en.wikipedia.org/wiki/Texas_Instruments_TMS320DSP 系列,其中char可能是16位。不允许更小。)

注意sizeof(char) == 1 根据定义因此通常被认为是不好的写作风格sizeof(char) or foo * sizeof(char)在你的代码中。

...我希望两个缓冲区的大小均为 1 字节/8 位。

这也是正确的(但见下文)。

  • 一个 ASCII 字符有 7 位长,我希望每个缓冲区能容纳两个字符。

This is not correct, for two reasons. First, nobody uses 7-bit ASCII anymore. Each character is in fact eight bits long. Second, two seven-bit characters do not fit into one eight-bit buffer. I see that there is some confusion on this point in the comments on the question, so let me attempt to explain further: Seven bits can represent 27 different values, just enough room for the 128 different characters defined by the original ASCII standard. Two seven-bit characters, together, can have 128 * 128 = 16384 = 214 different values; that requires 14 bits to represent, and will not fit into eight bits. You seem to have thought it was only 2 * 128 = 28, which would fit into eight bits, but that's not right; it would mean that once you saw the first character, there were only two possibilities for the second character, not 128.

  • 当我直接分配两个一个字节的缓冲区时,我希望它们在内存中直接相邻。因此,我预计每个地址之间的差异是 1(因为内存是按字节寻址的?),而不是我的小程序打印的 8。

正如您自己所观察到的,您的期望是不正确的。

malloc不需要将连续的分配彼此相邻;事实上,“这些分配是否彼此相邻”可能不是一个有意义的问题。 C 标准不遗余力地avoid要求有any不指向同一数组的两个指针之间的有意义比较。

现在,您正在一个具有“平面地址空间”的系统上工作,所以它is比较连续分配的指针是有意义的(前提是您在自己的大脑中进行操作,而不是使用代码),并且对于分配之间的间隙有一个逻辑解释,但首先我必须指出您打印了错误的地址:

printf("address, buffer1 %p\n", &buffer1);
printf("address, buffer2 %p\n", &buffer2);

这将打印地址指针变量,而不是地址buffers。你应该写

printf("address, buffer1 %p\n", (void *)buffer1);
printf("address, buffer2 %p\n", (void *)buffer2);

(演员阵容void *是必需的,因为printf接受一个变量参数列表。)如果你写了你会看到类似的输出

address, buffer1 0x55583d9bb010
address, buffer2 0x55583d9bb030

需要注意的重要一点是,这些分配的不同之处在于sixteen字节,不仅如此,它们都可以被 16 整除。

malloc需要产生的缓冲区是aligned根据需要any类型,即使您无法将该类型的值放入分配中。如果地址可以被某个字节数整除,则该地址与该字节数对齐。在您的系统上,最大对齐要求是 16;您可以通过运行该程序来确认这一点...

#include <stdalign.h>
#include <stddef.h>
#include <stdio.h>
int main(void) { printf("%zu\n", alignof(max_align_t)); return 0; }

这意味着返回的所有地址malloc必须能被 16 整除。因此,当你问malloc对于两个一字节缓冲区,它们之间必须留有十五字节的间隙。这does not意思是malloc将尺寸四舍五入; C 标准明确禁止您访问间隙中的字节。 (我不知道有任何现代的商业 CPU 可以强制执行该禁令,但调试工具如valgrind http://valgrind.org将会,并且已经有实验性的 CPU 设计可以做到这一点。此外,通常紧接在malloc块包含内部使用的数据malloc实施,您不得篡改。)

第二次分配后也存在类似的差距。

  • 由于它们在内存中直接相邻,我预计缓冲区 2 会溢出BB当我做strcpy(buffer1, BBBB);作为第一个BB写给buffer1其余的溢出到buffer2.

正如前面讨论的,它们在内存中并不直接相邻,并且每个 B 占用eight位。一个 B 写入您的第一个分配,接下来的 15 个写入两次分配之间的间隙,第 16 个写入第二个分配,之后还有 15 个写入间隙after第二次分配,最后一个 B 和一个 NUL 分配给超出的空间。

我只分配了 2 个字节(因为大小buffer1 and buffer2总共 2 个字节)。自从BBBBBBBBBBBBBBBBBBBBBBBBB两者都不适合buffer1 nor buffer2(因为两者都已经被填满),之后将溢出到下一个内存缓冲区buffer2。因为我没有分配它,所以我预计会出现分段错误。

我们已经讨论过为什么你的计算不正确,但是你did在第二次分配后一直写入到间隙末尾并进入“空间之外”,那么为什么没有段错误呢?这是因为,在操作系统原语级别,内存以称为“的单元”分配给应用程序。pages https://en.wikipedia.org/wiki/Page_(computer_memory)“,这比您要求的内存量要大。CPU只能检测缓冲区溢出,并在溢出跨越页面边界时触发分段错误。您做得还不够。我在我的电脑,非常相似,我需要写132 KB(一千字节是 1024 字节)(有些人说这应该被称为千字节;他们错了)超出 buffer1 的末尾会出现段错误。我的计算机上的每个页面只有 4 KB,但是malloc向操作系统请求更大块的内存,因为系统调用的成本很高。

没有收到提示的段错误does not意味着你很安全;你很有可能被打败malloc的内部数据,或“超越空间”中的另一个分配。如果我采用你的原始程序并添加一个调用free(buffer1)最后,它崩溃了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么此代码中的缓冲区溢出行为与我的预期不同? 的相关文章

  • 在 C/C++ 中获得正模数的最快方法

    通常在我的内部循环中 我需要以 环绕 方式索引数组 因此 例如 如果数组大小为 100 并且我的代码要求元素 2 则应该给它元素 98 高级语言 例如 Python 可以简单地使用my array index array size 但由于某
  • 何时使用 =default 使析构函数默认?

    尽管对构造函数使用 default 对我来说很清楚 即强制编译器在其他构造函数存在时创建默认构造函数 但我仍然无法理解这两种类型的析构函数之间的区别 那些使用 default 的 那些没有显式定义并由编译器自动生成的 我唯一想到的是 gro
  • 为什么在创建矩阵类时使用向量不好?

    对于我的矩阵类 我做了 template
  • 指向特征矩阵的指针数组

    我在代码中使用 Eigen 的 MatrixXd 矩阵 在某个时刻我需要一个 3D 矩阵 由于 Eigen 没有三维矩阵类型 因为它仅针对线性代数进行了优化 因此我创建了一个 MatrixXd 类型的指针数组 Eigen MatrixXd
  • 为什么这个没有特殊字符的正则表达式会匹配更长的字符串?

    我正在使用此方法来尝试查找匹配项 例如 Regex Match A2 TS OIL TS OIL RegexOptions IgnoreCase Success 我得到了真实的结果 我很困惑 我认为这应该返回 false 因为模式中没有特殊
  • 找不到 assimp-vc140-mt.dll ASSIMP

    我已经从以下位置下载了 Assimp 项目http assimp sourceforge net main downloads html http assimp sourceforge net main downloads html Ass
  • 单例模式和 std::unique_ptr

    std unique ptr唯一地控制它指向的对象 因此不使用引用计数 单例确保利用引用计数只能创建一个对象 那么会std unique ptr与单例执行相同 单例确保只有一个实例属于一种类型 A unique ptr确保只有一个智能指针到
  • 从 WebBrowser 控件 C# 获取滚动值

    我试图在 WebBrowser 控件中获取网页的 Y 滚动索引 但无法访问内置滚动条的值 有任何想法吗 对于标准模式下的 IE 使用文档类型 正如你所说 scrollTop是的财产元素 而不是 HtmlDocument htmlDoc th
  • 检查 RoutedEvent 是否有任何处理程序

    我有一个自定义 Button 类 当单击它时 打开特定窗口 它总是执行相同的操作 我添加了一个可以在按钮的 XAML 中分配的 Click 事件 就像常规按钮一样 当它被单击时 我想执行 Click 事件处理程序 如果已分配 否则我想执行默
  • 给出 5 个参数,但在终端中只得到 3 个参数

    我想将一个文件传递给一个c 程序 如果我在 IDE 中执行此操作 test string string lt test txt return argc 5 但在终端上我刚刚得到argc 3 看来 这是因为 什么是 lt 意思是 我正在使用
  • AES 输出是否小于输入?

    我想加密一个字符串并将其嵌入到 URL 中 因此我想确保加密的输出不大于输入 AES 是可行的方法吗 不可能创建任何始终会创建比输入更小的输出的算法 但可以将任何输出反转回输入 如果您允许 不大于输入 那么基本上您只是在谈论同构算法alwa
  • 运行选定的代码生成器时出错:“未将对象引用设置到对象的实例。”错误?

    我已经尝试了所有解决方案 例如修复 VS 2013 但没有用 当您通过右键单击控制器文件夹来创建控制器并添加控制器时 然后右键单击新创建的控制器的操作并选择添加视图 当我尝试创建视图时 就会发生这种情况 它不是一个新项目 而是一个现有项目
  • 如何通过 JsonConvert.DeserializeObject 在动态 JSON 中使用 null 条件运算符

    我正在使用 Newtonsoft 反序列化已知的 JSON 对象并从中检索一些值 如果存在 关键在于对象结构可能会不断变化 因此我使用动态来遍历结构并检索值 由于对象结构不断变化 我使用 null 条件运算符来遍历 JSON 代码看起来像这
  • 是否有相当于 Clang/LLVM 的 .spec 文件,在哪里可以找到参考?

    The gcc驱动程序可以配置为使用特定的链接器 特定的选项和其他细节 例如覆盖系统头 specs files 当前 截至撰写本文时 GCC 版本 4 9 0 的手册此处描述了规范文件 https gcc gnu org onlinedoc
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • Objective C (iphone) 关于发布的问题

    如果我创建一个视图 并将其添加为子视图并将其添加到数组中 是否必须释放它两次 UIView cat UIView alloc initWithFrame someFrame self view addSubview cat self ani
  • 使用taskkill停止Windows服务

    我需要帮助来使用 C 终止 Windows 服务 现在要终止该服务 请使用以下选项 从命令 sc queryex ServiceName 发现后PID服务的 taskkill pid 1234 exemple f 为了便于阅读 但如果您明白
  • 如何在 DropDownList 中保留空格 - ASP.net MVC Razor 视图

    我在视图中通过以下方式绑定我的模型 问题是我的项目文本是格式化文本 单词之间有空格 如下所示 123 First 234 00 123 AnotherItem 234 00 123 Second 234 00 我想保留此项目文本中的空格 即
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte
  • ASP.NET Core MVC 视图组件搜索路径

    在此处的文档中 https learn microsoft com en us aspnet core mvc views view components view aspnetcore 2 2 https learn microsoft

随机推荐