在不出现分段错误的情况下,可以超过分配的块多少字节?
By how many bytes is it possible to exceed an allocated block without getting segmentation fault?
我了解到,每当调用 malloc 时,OS 分配给程序的实际内存并不完全是请求的大小,而是四舍五入到页面大小。据我所知,页面大小是 1024 或 4096。
基于这个逻辑(如果错了就纠正我),超出我分配的块的写入不会总是导致分段错误,因为这个错误是由内核给出的(它给了我一个完整的页面并且没有'只要我呆在里面,我就不会在乎我做什么。
奇怪的是,在下面的程序中,我从 malloc 请求了 8 个字节,然后又写入了 80000 (sizeof(size_t) * 10000) 个字节,而没有出现分段错误。不过,我确实遇到了无效的读取和写入 valgrind 错误。
有人可以阐明这个话题吗?
#include <stdio.h>
#include <stdlib.h>
int main()
{
size_t *ptr = (size_t *)malloc(sizeof(size_t));
size_t forward = 10000;
ptr += forward;
*ptr = 8;
printf("%lu\n", *ptr);
ptr -= forward;
free(ptr);
return (0);
}
你混淆了两个截然不同的东西。如果您向 malloc
请求 1 个字节,它可能会从操作系统分配一个 4,096 字节的页面,但它可能只保留该块的 16 个字节用于此对 malloc
的调用。下一次调用 malloc
可能会从同一个 4,096 字节页面中获得另外 16 个字节。
您不能仅仅因为您没有遇到分段错误就假设您只访问了 space 实现已为您的块保留。您可以在用于其他目的的其他对象之上书写。
您假设 malloc
仅请求 OS 的一页用于您的分配,并且后续页面仍未映射。这不一定是真的。 malloc
通常会一次请求很多页,然后尽可能将它们用于以后的分配,从而减少系统调用并减少开销。
因此很可能您的分配后的几个页面也映射到您的进程,这就是您没有出现段错误的原因。相反,您覆盖了可能用于某些重要内容的内存。
确实,对于内部 C 库结构,malloc
可能在 main
开始之前请求了更大的块,并且您的分配只是放置在该块中。在我的测试中,使用 strace
查看 brk()
系统调用,这个块的大小为 33 页 (132 kB),而你的块位于该块的第一页中。因此,您的 80 kB 超限仍在该映射区域内。
所以标题问题的答案是“这取决于你的块在它的页面中的位置,以及哪些周围的页面恰好被映射”。这些又取决于 malloc
使用的精确算法以及您的代码或库代码完成的其他分配和释放的模式,直到那时,您才能真正预测到 none。原则上可以找出映射了哪些页面(例如,通过解析 /proc/self/maps
在 Linux 上),但是随着代码或库函数分配和释放内存,这可能会发生不可预测的变化。所以实际的答案是“你不知道”。
基本上,当您在 malloc
ed 内存的边界之外写入时,您不应该对会发生什么或不会发生什么做出任何假设。只有当你自己 mmap
ed 和 mprotect
ed 内存时,你才能真正确定会出现段错误。
我了解到,每当调用 malloc 时,OS 分配给程序的实际内存并不完全是请求的大小,而是四舍五入到页面大小。据我所知,页面大小是 1024 或 4096。
基于这个逻辑(如果错了就纠正我),超出我分配的块的写入不会总是导致分段错误,因为这个错误是由内核给出的(它给了我一个完整的页面并且没有'只要我呆在里面,我就不会在乎我做什么。
奇怪的是,在下面的程序中,我从 malloc 请求了 8 个字节,然后又写入了 80000 (sizeof(size_t) * 10000) 个字节,而没有出现分段错误。不过,我确实遇到了无效的读取和写入 valgrind 错误。
有人可以阐明这个话题吗?
#include <stdio.h>
#include <stdlib.h>
int main()
{
size_t *ptr = (size_t *)malloc(sizeof(size_t));
size_t forward = 10000;
ptr += forward;
*ptr = 8;
printf("%lu\n", *ptr);
ptr -= forward;
free(ptr);
return (0);
}
你混淆了两个截然不同的东西。如果您向 malloc
请求 1 个字节,它可能会从操作系统分配一个 4,096 字节的页面,但它可能只保留该块的 16 个字节用于此对 malloc
的调用。下一次调用 malloc
可能会从同一个 4,096 字节页面中获得另外 16 个字节。
您不能仅仅因为您没有遇到分段错误就假设您只访问了 space 实现已为您的块保留。您可以在用于其他目的的其他对象之上书写。
您假设 malloc
仅请求 OS 的一页用于您的分配,并且后续页面仍未映射。这不一定是真的。 malloc
通常会一次请求很多页,然后尽可能将它们用于以后的分配,从而减少系统调用并减少开销。
因此很可能您的分配后的几个页面也映射到您的进程,这就是您没有出现段错误的原因。相反,您覆盖了可能用于某些重要内容的内存。
确实,对于内部 C 库结构,malloc
可能在 main
开始之前请求了更大的块,并且您的分配只是放置在该块中。在我的测试中,使用 strace
查看 brk()
系统调用,这个块的大小为 33 页 (132 kB),而你的块位于该块的第一页中。因此,您的 80 kB 超限仍在该映射区域内。
所以标题问题的答案是“这取决于你的块在它的页面中的位置,以及哪些周围的页面恰好被映射”。这些又取决于 malloc
使用的精确算法以及您的代码或库代码完成的其他分配和释放的模式,直到那时,您才能真正预测到 none。原则上可以找出映射了哪些页面(例如,通过解析 /proc/self/maps
在 Linux 上),但是随着代码或库函数分配和释放内存,这可能会发生不可预测的变化。所以实际的答案是“你不知道”。
基本上,当您在 malloc
ed 内存的边界之外写入时,您不应该对会发生什么或不会发生什么做出任何假设。只有当你自己 mmap
ed 和 mprotect
ed 内存时,你才能真正确定会出现段错误。