如果指针作为大小传入,为什么 malloc 会覆盖以前的分配?

Why does malloc overwrite previous allocation if pointer is passed in as size?

我有以下 C 代码,但我不太理解这里 malloc 的行为:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
        size_t* v3;
        char *message;
        size_t size;
        v3 = malloc(10uLL);
        *v3 = 1LL;
        printf("first address: %p\n", (void *)v3);
        size = 0;
        scanf("%lu", &size);
        message = malloc(size);
        read(0, message, size);
        message[size-1] = 0;
        write(1, message, size);
        if(!*v3) printf("you win");
        else printf("you didn't win");
        return 0;
}

所以程序分配了一些内存,我们打印了地址。现在,如果我将此地址用作数字并向其加 1,然后将其作为下一个 malloc 的大小提供,那么当 message[size-1] = 0 发生时,它基本上会溢出并修改 v3。它被设置为 1LL 但之后它是 0 并且打印“你赢了”。

谁能解释一下这是怎么发生的?

正如评论中所建议的,这是我 运行 程序时发生的情况:

第一个输出是:first address: 0x5581cb0646b0 然后我打开一个 python shell 并执行: int("0x5581cb0646b0", 16) + 1 将十六进制值转换为数字并向其添加 1 得到 94015945328305

我将此提供给 scanf 然后 read 运行s 但消息在这里可以是任何内容,不会改变发生的事情。

最后,消息和“你赢了”被打印出来。 (但请注意 *v3 应该是 1LL 而不是 0

v3分配了某个内存块,之后你再分配一个内存块给message。 很有可能它们是连续的内存块并且消息是 8 字节通过 v3,但是你不应该依赖这种行为,因为 malloc 可以从它认为合适的任何地方给你一个指向地址的指针,所以它绝对是未定义的应该发生什么 仅仅因为这是您得到的结果并不意味着这是您每次都应该依赖的结果。

另一件需要考虑的事情是元数据 malloc 的指针可能持有什么,这取决于 malloc 的实现并且对“我们”用户是隐藏的,malloc 当然可以在分配的指针末尾为所有持有元数据我们知道,如果您想真正测试 malloc 而不会导致未定义的行为,您应该 运行 它与 vlg 一起确保您没有做任何可能产生不可靠结果的事情。

您介意解释一下您在此代码中究竟想做什么以及您是如何做到的吗?

经过更多调试和研究,我找到了解决方案。因此,就上下文而言,这是一个 CTF 挑战,我试图了解如何利用它。原来我错过的关键点是,如果你获取屏幕上打印的 v3 的地址并将 1 添加到它并将其传递给 scanf 然后用作第二个 malloc 的尺寸太大,malloc 将 return 0。但是,message[size - 1] 基本上只是 0 + size(这是 v3 + 1 的地址)-1 然后就是 v3 的确切地址这使得可以将值更改为 0.