【问题标题】:Linux malloc segmentation fault missingLinux malloc 分段错误丢失
【发布时间】:2016-11-10 08:18:02
【问题描述】:

包含的程序应该在 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)

【问题讨论】:

  • 它将写入预期的结构之外,但可能不会写入堆栈。为什么会出现段错误?
  • 这就是未定义行为的问题。由于内存分配器只需要遵循一些规则,它们几乎可以做任何其他可以想象的事情。在这种情况下,您的 malloc 可能会分配更大的内存区域,因此您可以写入它。但你不能指望这一点。

标签: linux kernel memory-management


【解决方案1】:

内存分配器几乎可以做任何它想做的事情。这就是为什么这不符合您的预期。

以下是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)   

【讨论】:

    【解决方案2】:

    未定义的行为是未定义的 - 不需要检测报告错误。大多数实现更注重性能而不是错误检测。

    如果您想检查您的程序的正确性(提示:您会这样做,就像我们其他人一样!),那么您将需要使用 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 blkfmtsize_t,所以你需要 %zd 转换。

    此外,永远不要转换malloc 的结果,因为这会掩盖其他问题。一个好的习惯用法是p = malloc(sizeof *p) - 这显然是正确的,甚至不必查找p 的类型。

    另外,您不需要包含&lt;unistd.h&gt;

    简化示例:

    #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;
    }
    

    【讨论】:

      【解决方案3】:

      malloc 会通过 page(4096) 从内核获取更大的空间,所以如果指针指向一个有效的空间,它就不会崩溃。

      【讨论】:

        猜你喜欢
        • 2014-03-29
        • 1970-01-01
        • 2015-01-29
        • 2011-02-13
        • 1970-01-01
        • 1970-01-01
        • 2021-11-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多