memcpy(dst, src, n) 之后的 free(src) 导致段错误

free(src) after memcpy(dst, src, n) causes a segfault

考虑以下代码:

int main(int argc, char* argv[])
{
    int prt = 6;
    serial_port *cprt = open_comport(prt);

    int n;

    while(TRUE)
    {
        ubx_raw *msg = malloc(sizeof(ubx_raw));
        uint8_t *buf = malloc(1024*sizeof(uint8_t));
        n = RS232_PollComport(cprt->nr, buf, 1024);

        msg = ubx_acquire_frombuf(buf, n);
        //PROBLEM
        free(buf);
        if (msg == NULL)
        {
            printf("Message scrambled or no message in the buffer!\n");
            free(msg);
        }
        else
        {
            printf("Length: %" PRIu16 "\n", msg->length);
        }

        free(buf);
        Sleep(1000);
        if (msg == NULL)
        {
            free(msg);
        }
    }

    return 0;
}

具有函数

ubx_raw *ubx_acquire_frombuf(uint8_t *buf, int size)
{
    ubx_raw *msg = malloc(sizeof(ubx_raw));
    int n = 0;
    //sweep through bytes
    while (n < size - 1)
    {
        //check if start of message
        if ((buf[0] == UBX_SYNC1) && (buf[1] == UBX_SYNC2))
        {
            //put stuff into msg
            msg->length = ((uint16_t)*(buf+4));
            //check if full message available, otherwise
            //reduce amount to memcpy
            if (msg->length < size)
            {
                size = msg->length;
            }
            //PROBLEMATIC
            memcpy(&msg->data, &buf, size);
            return msg;
        }
        else
        {
            n++;
            continue;
        }
    }
    return NULL;
}

每当调用 ubx_acquire_frombuf() 时(在 main 中的 while 循环开始附近),随后的 free(buf) 会给出一个段错误。显然,该函数中的 buf 发生了一些不好的事情。我知道你不能 free() 没有 malloc() 的东西,你也不能 free() 东西两次。其实,因为指针是按值传递的,所以ubx_acquire_frombuf()里面发生的事情应该和main无关吧?

无论如何,注释掉 PROBLEMATIC memcpy()(在 ubx_acquire_frombuf() 中)会删除段错误。为何如此?我还想用那个memcpy!任何人都可以阐明正在发生的事情吗? memcpy() 不应该编辑它正在复制的来源(即 buf),对吧?

P.S。很抱歉没有一个最小的例子,但我无法从头开始重现问题。我不确定发生了什么,但在 ubx_acquire_frombuf() 中没有任何异常(即调用我编写的其他函数)所以这与最小示例相差不远。


编辑:根据大众需求:

typedef struct {
  uint8_t *data;
  uint16_t length;
} ubx_raw;

几点可疑; 1) 为什么我们在 memcpy(&msg->data, &buf, size);

中使用 &buf

2) 为什么我们不在

中做 buff++
n++;
buff++;//should be added
continue; //This is useless

3) 当你做 ubx_raw *msg = malloc(sizeof(ubx_raw));

我不确定分配给 msg->data 的内存是多少。或者初始化为垃圾指针。

我看到很多痛点,但首先需要清除这些痛点。

我的猜测是 ubx_raw 的声明是罪魁祸首。 例如,如果 uxb_raw 定义为

struct uxb_raw {
    uint8_t *data;
};

那么 sizeof(uxb_raw) 操作只是 return 指针的大小,而不是 uxb_raw.data 应该指向的任何大小。但是,如果您已声明

#define MAX_DATA_ARRAY_LENGTH 15
struct uxb_raw {
    uint8_t data[MAX_DATA_ARRAY_LENGTH];
};

然后 sizeof(uxb_raw) 将 return 正确的字节数。如果你想要一个 malloced 数据数组,例如,在你的 main 中,你可能想像这样创建 msg

struct uxb_raw msg = {.data = malloc(MAX_DATA_ARRAY_LENGTH * sizeof(uint8_t))}; 

在您的 ubx_acquire_frombuf 函数中,在复制数据之前检查 *buf 数组大小不大于 MAX_DATA_ARRAY_LENGTH

您标记为有问题的代码确实有问题:

// PROBLEMATIC
memcpy(&msg->data, &buf, size);

问题是您正在将 size 字节复制到字段 msg->data (因为您正在将 msg->data 的地址传递给 memcpy),但该字段只是一个指针它的尺寸很小。因此 memcpy 将覆盖 ubx_raw 结构的其余部分,然后覆盖内存中它后面的任何内容,其中将包括一些 malloc 的内部会计信息。

你的意思可能是

// PROBLEMATIC
memcpy(msg->data, &buf, size);

但这仍然是个问题,因为msg->data从未被初始化过。所以一个更可能的解决方案是:

// NOT PROBLEMATIC but don't forget to free(msg->data) before free(msg).
msg->data = malloc(size); // Check for non-NULL
memcpy(msg->data, &buf, size);