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

 int main()
 {
     char *name;
     char *command;
     name=(char *)malloc(10);
     command=(char *)malloc(128);
     printf("address of name is : %d\n",name);
     printf("address of command is:%d\n",command);
     printf("Distance between addresses is :%d\n",command-name);
     printf("Enter your name:");
     gets(name);
     printf("Hello %s\n",name);
     system(command);
 }

分配恒定数量的内存(缓冲区大小)和两个地址之间的距离(相邻的内存块)有什么区别?在此示例中,名称和命令之间的差异为 16 字节,名称的缓冲区大小为 10 字节。哪一个会触发缓冲区溢出?

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

// typedef size_t u_long;

int main(){
    u_long distance;
    char *buf1= (char *)malloc(16);
    char *buf2= (char *)malloc(16);
    distance= (u_long)buf2 - (u_long)buf1;
    printf("buf1 = %p\nbuf2 = %p\ndistance = 0x%x bytes\n",
           buf1, buf2, distance);
    memset(buf2, 'A', 15); buf2[15]='\0';
    printf("before overflow buf2 = %s\n", buf2);
    memset(buf1, 'B', (8+distance));
    printf("after overflow buf2 = %s\n", buf2);
    return 0;
}
4

2 回答 2

0

当我在带有 GCC 4.7.1 的 Mac OS X 10.8.3 上运行第二个程序的非常温和的编辑版本时,0x%x更改为和未注释0x%zX的 typedef ,输出为:u_long

buf1 = 0x7fe8d0c000e0
buf2 = 0x7fe8d0c03a00
distance = 0x3920 bytes
before overflow buf2 = AAAAAAAAAAAAAAA
Abort trap: 6

这两个分配并没有相互靠近,这是完全允许的,但(坦率地说)不是我期望看到的。大约 14 KiB 的写入肯定会调用未定义的行为。

您无法假设在可移植代码中将分配内存的位置。如果您希望调整到某个特定版本malloc(),您可以这样做,但这会限制您的可移植性。

然而,在像 Mac 这样的 64 位系统上,当您进行两次 1 字节的单独分配时,它们通常相隔 16 字节。在您的中间添加此代码:

    char *buffer[4];
    for (int i = 0; i < 4; i++)
    {
        buffer[i] = (char *)malloc(1);
        printf("buffer[%i] = %p\n", i, buffer[i]);
    }

给出了输出:

buf1 = 0x7fe821c000e0
buf2 = 0x7fe821c03a00
distance = 0x3920 bytes
buffer[0] = 0x7fe821c03a10
buffer[1] = 0x7fe821c03a20
buffer[2] = 0x7fe821c03a30
buffer[3] = 0x7fe821c03a40
before overflow buf2 = AAAAAAAAAAAAAAA
Abort trap: 6

在循环中每次请求 16 个字节时,我也得到了相同的输出。那很有意思; 这意味着没有直接与这些内存块相邻的控制信息(这些分配之间没有控制信息的空间——它可能存储在 和 之间的一些空间中buf1buf2。在其他 64 位系统上,我看到最小分配为 32 字节,其中一些空间用于控制信息。也就是说,1 字节的连续请求的地址malloc()将产生相隔 32 字节的地址,但 32 字节的连续请求将产生相隔 64 字节的地址。

在 32 位 Linux 机器上,请求 1 字节和 8 字节,我得到了分配:

buffer[0] = 0x804a008
buffer[1] = 0x804a018
buffer[2] = 0x804a028
buffer[3] = 0x804a038

对于 16 个字节的请求,我得到:

buffer[0] = 0x804a008
buffer[1] = 0x804a020
buffer[2] = 0x804a038
buffer[3] = 0x804a050

如您所见,在第一种情况下,分配相隔 16 个字节;在第二个,24 个字节。额外的 8 个字节是控制开销。

当你请求 N 字节的空间并malloc()返回一个指针 P 时,你就有了访问地址范围内数据的显式权限:

 (char *)P + 0 .. (char *)P + N - 1 (inclusive)

该范围之外的任何访问都会导致未定义的行为。未定义的行为可能包括看似有效。


在您的第一个程序中,您无需初始化指向system(command)的数据即可执行。command这不太可能导致幸福。

于 2013-06-02T22:15:22.067 回答
0

因为namecommand是通过对 的单独调用分配的malloc(),所以严格来说,它们的地址之间的差异是没有意义的。指针运算仅适用于同一数组或同一分配块中的地址。当然,减去两个指针会给你一些数字,你不能用它来做任何事情。没有理由相信它malloc()会使用相邻的内存块,甚至后面的分配将具有比早期分配更高的地址。它可以自由分配,但它认为合适。

你分配了 10 个字节,所以你可以使用 10 个字节。而已。

于 2013-06-02T21:02:27.917 回答