Valgrind memcpy 大小为 8 的无效写入 (uintptr_t *)

Valgrind memcpy Invalid write of size 8 (uintptr_t *)

我对 memcpyvalgrind 有疑问,告诉我 Invalid write of size 8。 我已经到了弄清楚错误代码在哪里的地步,但我不知道为什么它是错误的...... 我知道还有其他与此相关的问题,但它们并没有真正帮助我。

以下是我在某种程度上“通用”堆栈上的方法中最重要的部分的摘录,当我的常规值是 uintptr_t.

类型时

下面是我使用的两个定义:

// default stack batch size
#define STACK_BATCH_DEFAULT 8
// size of one value in the stack
#define STACK_SIZEOF_ONE    sizeof(uintptr_t)

栈的结构如下:

typedef struct Stack
{
    size_t count;       // count of values in the stack
    size_t size;        // size of one value in bytes
    size_t alloced;     // allocated count
    uintptr_t *value;   // the values
    int batch;          // memory gets allocated in those batches
}
Stack;

我有一个栈的初始化函数:

bool stack_init(Stack *stack, size_t size, int batch)
{
    if(!stack) return false;
    stack->batch = batch ? batch : STACK_BATCH_DEFAULT;
    stack->size = size;
    stack->count = 0;
    stack->value = 0;
    stack->alloced = 0;
    return true;
}

然后 stack_push 函数,其中 valgrind 抛出错误 Invalid write of size 8:

bool stack_push(Stack *stack, uintptr_t *value)
{
    if(!stack || !value) return false;
    // calculate required amount of elements
    size_t required = stack->batch * (stack->count / stack->batch + 1);
    // allocate more memory if we need to
    if(required > stack->alloced)
    {
        uintptr_t *tmp = realloc(stack->value, required * stack->size);
        if(!tmp) return false;
        stack->value = tmp;
        stack->alloced = required;
    }
    // set the value
    if(stack->size > STACK_SIZEOF_ONE)
    {
        memcpy(stack->value + stack->size * stack->count, value, stack->size);  // <--- valgrind throws the error here
    }
    else
    {
        stack->value[stack->count] = *value;
    }
    // increment count
    stack->count++;
    return true;
}

然后在我的程序中调用函数如下:

Stack stack = {0};  
stack_init(&stack, sizeof(SomeStruct), 0);
/* ... */
SomeStruct push = { // this is a struct that is larger than STACK_SIZEOF_ONE
    .int_a = 0,
    .int_b = 0,
    .int_c = 0,
    .id = 0,
    .pt = pointer_to_struct,    // it is a pointer to some other struct that was allocated beforehand
};
stack_push(&stack, (uintptr_t *)&push);

对于 universal 我的意思是我也可以有一个 regular stack:

Stack stack = {0};
stack_init(&stack, sizeof(uintptr_t), 0);
/* ... */
uintptr_t a = 100;
stack_push(&stack, &a);

此外,如果有任何 should/could 需要改进的地方,我愿意听取一般提示和建议:)

编辑:下面是一个可运行的代码。

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

// default stack batch size
#define STACK_BATCH_DEFAULT 8
// size of one value in the stack
#define STACK_SIZEOF_ONE    sizeof(uintptr_t)

#define TESTCOUNT   10
#define MAX_BUF     16

typedef struct Stack
{
    size_t count;       // count of values in the stack
    size_t size;        // size of one value in bytes
    size_t alloced;     // allocated count
    uintptr_t *value;   // the values
    int batch;          // memory gets allocated in those batches
}
Stack;

typedef struct SomeStruct
{
    size_t a;
    size_t b;
    size_t c;
    size_t id;
    char *str;
}
SomeStruct;

bool stack_init(Stack *stack, size_t size, int batch)
{
    if(!stack) return false;
    stack->batch = batch ? batch : STACK_BATCH_DEFAULT;
    stack->size = size;
    stack->count = 0;
    stack->value = 0;
    stack->alloced = 0;
    return true;
}

bool stack_push(Stack *stack, uintptr_t *value)
{
    if(!stack || !value) return false;
    // calculate required amount of elements
    size_t required = stack->batch * (stack->count / stack->batch + 1);
    // allocate more memory if we need to
    if(required > stack->alloced)
    {
        uintptr_t *tmp = realloc(stack->value, required * stack->size);
        if(!tmp) return false;
        stack->value = tmp;
        stack->alloced = required;
    }
    // set the value
    if(stack->size > STACK_SIZEOF_ONE)
    {
        memcpy(stack->value + stack->size * stack->count, value, stack->size);  // <--- valgrind throws the error here
    }
    else
    {
        stack->value[stack->count] = *value;
    }
    // increment count
    stack->count++;
    return true;
}

bool stack_pop(Stack *stack, uintptr_t *value)
{
    if(!stack) return false;
    if(!stack->count) return false;

    // decrement count of elements
    stack->count--;

    // return the value if we have an address
    if(value)
    {
        if(stack->size > STACK_SIZEOF_ONE)
        {
            memcpy(value, stack->value + stack->size * stack->count, stack->size);
        }
        else
        {
            *value = stack->value[stack->count];
        }
    }
    
    int required = stack->batch * (stack->count / stack->batch + 1);
    if(required < stack->alloced)
    {
        uintptr_t *tmp = realloc(stack->value, required * stack->size);
        if(!tmp) return false;
        stack->value = tmp;
        stack->alloced = required;
    }
    if(!stack->value) return false;

    return true;
}

int main(void)
{
    // initialize variables
    bool valid = false;
    Stack default_stack = {0};
    Stack some_stack = {0};

    // initialize stacks
    stack_init(&default_stack, sizeof(uintptr_t), 0);
    stack_init(&some_stack, sizeof(SomeStruct), 0);

    // test default case - push
    printf("Testing the default case, pushing...\n");
    for(int i = 0; i < TESTCOUNT; i++)
    {
        uintptr_t push = i;
        valid = stack_push(&default_stack, &push);
        if(!valid) return -1;
    }
    // ...now pop
    printf("Testing the default case, popping...\n");
    do
    {
        uintptr_t pop = 0;
        valid = stack_pop(&default_stack, &pop);
        if(valid) printf("%llu,", pop);
    }
    while(valid);
    printf("\n");

    // test some case - push
    printf("Testing some case, pushing...\n");
    for(int i = 0; i < TESTCOUNT; i++)
    {
        // generate the push struct
        SomeStruct push = {
            .a = i * 10,
            .b = i * 100,
            .c = i * 1000,
            .id = i,
            .str = 0,
        };
        // allocate a string
        push.str = malloc(MAX_BUF + 1);
        snprintf(push.str, MAX_BUF, "%d", i);
        // push
        valid = stack_push(&some_stack, (uintptr_t *)&push);
        if(!valid) return -1;
    }
    // ...now pop
    printf("Testing some case, popping...\n");
    do
    {
        SomeStruct pop = {0};
        valid = stack_pop(&some_stack, (uintptr_t *)&pop);
        if(valid)
        {
            printf("a=%d,b=%d,c=%d,id=%d,str=%s\n", pop.a, pop.b, pop.c, pop.id, pop.str);
            free(pop.str);
        }
    }
    while(valid);
    printf("\n");

    /* leave out free functions for this example.... */

    return 0;
}

几小时后我想通了 :D 错误发生是因为我很少做指针运算...简而言之,我假设它总是用 byte 计算.

让我们看一下包含以下内容的行:

memcpy(stack->value + stack->size * stack->count, value, stack->size);

...并将其分解,因此更具可读性。而且,我什至会在其中添加一个方便的花花公子评论:

size_t offset = stack->size * stack->count; // offset in bytes
void *dest = stack->value + offset;
void *src = value;
memcpy(dest, src, stack->size);

现在 专业的 C 程序员应该立即发现问题。它与 stack->value + offset 的计算有关,它应该在 字节中添加偏移量 但事实并非如此,因为 stack->valueuintptr_t * 类型并且不是 uint8_t *.

类型

所以为了修复它,我用这一行替换了它:

void *dest = (uint8_t *)stack->value + offset;

代码有效。