包含的程序应该在Linux中产生分段错误,但它不会:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/* This program should produce a segmentation error */
/* but it doesn't */
/* Therefore memory bounds are not being checked by the kernel */
typedef struct {
double x;
} blkfmt;
int main()
{
blkfmt *blk;
unsigned char *p;
blk = (blkfmt *) malloc(sizeof(blkfmt));
p = (unsigned char *) blk + 16384;
/* this assignment should produce a segmentation error */
*p = (unsigned char) 0xff;
/* this print statement should produce a segmentation error */
printf("%02x %d\n", *p, sizeof(blkfmt));
free(blk);
return(0);
} /* main */
OBJ=bug.o
CC=gcc
CFLAGS=-c -Wall -O2
LDFLAGS=
bug: $(OBJ)
$(CC) -Wall -O2 $(OBJ) -o bug $(LDFLAGS)
bug.o: bug.c
$(CC) $(CFLAGS) bug.c
clean:
rm -f bug $(OBJ)
答案 0 :(得分:2)
内存分配器几乎可以做任何想做的事情。这就是为什么这不符合您的预期。
以下是运行程序的strace
的输出。在其中,您可以看到brk
系统调用用于为程序分配0x21000个字节。这是135,168字节。远远超过16,384你的程序添加到返回的指针。
execve("./malloc-outside-test", ["./malloc-outside-test"], [/* 62 vars */]) = 0
brk(NULL) = 0x1211000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2efad76000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=183184, ...}) = 0
mmap(NULL, 183184, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f2efad49000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\10\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2089496, ...}) = 0
mmap(NULL, 3938656, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2efa792000
mprotect(0x7f2efa94b000, 2093056, PROT_NONE) = 0
mmap(0x7f2efab4a000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b8000) = 0x7f2efab4a000
mmap(0x7f2efab50000, 14688, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2efab50000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2efad48000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2efad47000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2efad46000
arch_prctl(ARCH_SET_FS, 0x7f2efad47700) = 0
mprotect(0x7f2efab4a000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7f2efad77000, 4096, PROT_READ) = 0
munmap(0x7f2efad49000, 183184) = 0
brk(NULL) = 0x1211000
brk(0x1232000) = 0x1232000
brk(NULL) = 0x1232000
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
write(1, "ff 8\n", 5) = 5
exit_group(0)
答案 1 :(得分:1)
未定义未定义的行为 - 没有要求检测报告错误。大多数实现都支持性能而不是错误检测。
如果你想检查你的程序的正确性(提示:你这样做,就像我们其他人一样!),那么你想要使用Valgrind。当我使用您的程序运行Valgrind时,它会报告您的错误:
==26331== Invalid write of size 1
==26331== at 0x40059E: main (38255191.c:21)
==26331== Address 0x51de040 is 16,304 bytes inside an unallocated block of size 4,194,128 in arena "client"
==26331==
==26331== Invalid read of size 1
==26331== at 0x4005A5: main (38255191.c:23)
==26331== Address 0x51de040 is 16,304 bytes inside an unallocated block of size 4,194,128 in arena "client"
==26331==
顺便说一句,我注意到你的代码中存在一些问题。 sizeof blkfmt
是size_t
,因此您需要进行%zd
转换。
此外,永远不应该施放malloc
的结果,因为这可以掩盖其他问题。一个好的习语是p = malloc(sizeof *p)
- 这显然是正确的,甚至无需查找p
的类型。
此外,您还不需要加入<unistd.h>
。
简化示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
double *blk = malloc(sizeof *blk);;
unsigned char *p = malloc(sizeof *blk);
p = (unsigned char*)blk + 16384;
/* this assignment writes to unallocated memory */
*p = 0xff;
/* this reads from unallocated memory */
printf("%02x %zd\n", *p, sizeof *blk);
free(blk);
return 0;
}
答案 2 :(得分:0)
malloc将逐页从内核获取更大的空间(4096),因此如果指针指向有效空间,它将不会崩溃。