【问题标题】:Getting valgrind error in C code在 C 代码中出现 valgrind 错误
【发布时间】:2018-01-13 21:06:21
【问题描述】:

我正在使用 Valgrind 对小型应用程序的 C 代码进行内存分析,它使用 DFS 方法找出图中的所有路径。但是我仍然遇到一些错误,主要是在这部分代码中:

int process_edges(VoidStack *edges, char *buffer)
{
    char weight[DATE_LENGTH] = "";
    char min_weight[DATE_LENGTH] = "", max_weight[DATE_LENGTH] = "";

    int metric;

    VoidStack *reverse = malloc(sizeof(VoidStack));
    void_stack_new(reverse, DATE_LENGTH);

    /* Create reverse stack to have edges in correct order */
    while (edges->loglength != 0)
    {
         void_stack_pop(edges, weight);
         void_stack_push(reverse, weight);
    }

    if (reverse->loglength >= 1)
    {
        void_stack_pop(reverse, weight);

        strcpy(max_weight, weight);
        strcpy(min_weight, weight);

        sprintf(buffer + strlen(buffer), "%s", weight);

        void_stack_push(edges, weight);
}

我得到了这些 valgrind 错误:

==1399== Conditional jump or move depends on uninitialised value(s)
==1399==    at 0x4C2C2AB: strcpy (vg_replace_strmem.c:458)
==1399==    by 0x4010D5: process_edges (in /home/adam/C/dfs.out)
==1399==    by 0x401508: dfs (in /home/adam/C/dfs.out)
==1399==    by 0x4015A9: dfs (in /home/adam/C/dfs.out)
==1399==    by 0x40172A: all_paths (in /home/adam/C/dfs.out)
==1399==    by 0x401AEC: main (in /home/adam/C/dfs.out) 
==1399==  Uninitialised value was created by a stack allocation
==1399==    at 0x40100D: process_edges (in /home/adam/C/dfs.out) 
==1399== 
==1399== Conditional jump or move depends on uninitialised value(s)
==1399==    at 0x4C2C2AB: strcpy (vg_replace_strmem.c:458)
==1399==    by 0x4010E8: process_edges (in /home/adam/C/dfs.out)
==1399==    by 0x401508: dfs (in /home/adam/C/dfs.out)
==1399==    by 0x4015A9: dfs (in /home/adam/C/dfs.out)
==1399==    by 0x40172A: all_paths (in /home/adam/C/dfs.out)
==1399==    by 0x401AEC: main (in /home/adam/C/dfs.out)
==1399==  Uninitialised value was created by a stack allocation
==1399==    at 0x40100D: process_edges (in /home/adam/C/dfs.out)

我不知道如何修复这些错误。

我正在使用这些参数运行 Valgrind

--leak-check=full --track-origins=yes --show-reachable=yes

完整代码可在https://github.com/AdamPalaxo/KIV-PC获取。

编辑:

void_stack_newvoid_stack_pushvoid_stack_pop 函数添加了代码。

/* Creates new stack with given element size, allocates memory */
void void_stack_new(VoidStack *s, int element_size)
{
    s->element_size = element_size;
    s->loglength = 0;
    s->allocated_length = 4;
    s->elements = malloc((size_t) 4 * element_size);
}

/* Grows stack in case when maximal length of stack would be exceeded */
static void stack_grow(VoidStack *s)
{
    s->allocated_length *= 2;
    s->elements = realloc(s->elements, (size_t) s->allocated_length * s->element_size);
}

/* Pushes new element to stack on given address */
void void_stack_push(VoidStack *s, void *element_address)
{
    void *target;

    if(s->loglength == s->allocated_length)
    {
        stack_grow(s);
    }

    target = (char *)s->elements + s->loglength * s->element_size;
    memcpy(target, element_address, (size_t) s->element_size);
    s->loglength++;
}

/* Pops element from top of stack to given address */
void void_stack_pop(VoidStack *s, void *element_address)
{
    void *source;

    s->loglength--;
    source = (char *) s->elements + (s->loglength) * s->element_size;
    memcpy(element_address, source, (size_t) s->element_size);

} 

【问题讨论】:

  • 您确定void_stack_pushvoid_stack_pop 没有超出限制吗? minimal verifiable example 在这里会有所帮助。
  • @Pablo 我为这些函数添加了代码,但我认为(并希望)它们不会超出限制
  • 你应该检查错误,检查realloc返回NULL,你的堆栈不是空的/满的。除此之外,功能看起来还可以。但是,我仍然不能保证它们正确运行,因为我不知道您是如何初始化堆栈的。例如,s->element_size 持有什么值?如果是字符串(例如weight),您是否考虑使用\0 终止字节?真的所有字符串的长度都相同(您的堆栈希望输入始终具有相同的大小)吗?你如何初始化你的堆栈?
  • 好点,谢谢。我还添加了函数void_stack_new,用于初始化堆栈。值 s->element_size 包含 11,因为输入数据的格式为 YYYY-MM-DD 加上我正在考虑 \0 终止字节。所以堆栈总是期望输入总是相同的大小。
  • 我不太喜欢你的void_stack_new 功能。 allocated_length 是什么,您的堆栈开始的元素数?如果是这样,malloc 应该如下所示:malloc(s->allocated_length * element_size)

标签: c memory-management valgrind


【解决方案1】:

OP 在评论中发表了这篇文章

好点,谢谢。我还添加了函数void_stack_new,用于初始化堆栈。 值s->element_size 持有11,因为输入数据格式为YYYY-MM-DD 加上我 考虑到\0 终止字节。所以堆栈总是期望输入是 总是相同的大小。

我想,我解决了这个问题。我使用了你的堆栈代码并自己做了一些测试

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

typedef struct stack {
    int element_size;
    int loglength;
    int allocated_length;
    void *elements;
} VoidStack;

// I did copy & paste of your posted code
// not showing it here because it makes the code
// too long

void void_stack_print(VoidStack *s)
{
    char el[11];
    printf("Void Stack, element size: %d, #elements: %d, max #elements: %d\n",
            s->element_size, s->loglength, s->allocated_length);

    printf("Elements as strings:\n");
    for(int i = 0; i < s->loglength; ++i)
    {
        memcpy(el, ((char*) s->elements) + s->element_size * i, s->element_size);
        printf(" - %s\n", el);
    }

    puts("");
}

int main(void)
{
    VoidStack stack;

    void_stack_new(&stack, 11);

    void_stack_push(&stack, "2012-01-01");
    void_stack_push(&stack, "2012-01-02");
    void_stack_push(&stack, "2012-01-03");
    void_stack_push(&stack, "2012-01-04");

    void_stack_print(&stack);

    void_stack_push(&stack, "2013-01-01");
    void_stack_push(&stack, "2013-01-02");

    void_stack_print(&stack);

    char date[11], copy[11];

    void_stack_pop(&stack, date);
    strcpy(copy, date);
    printf("date: %s, copy: %s\n", date, copy);
    void_stack_print(&stack);

    void_stack_pop(&stack, date);
    strcpy(copy, date);
    printf("date: %s, copy: %s\n", date, copy);
    void_stack_print(&stack);


    free(stack.elements);
    return 0;
}

我的 valgrind 向我展示了这一点:

==748== Memcheck, a memory error detector
==748== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==748== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==748== Command: ./a
==748== 
Void Stack, element size: 11, #elements: 4, max #elements: 4
Elements as strings:
 - 2012-01-01
 - 2012-01-02
 - 2012-01-03
 - 2012-01-04

Void Stack, element size: 11, #elements: 6, max #elements: 8
Elements as strings:
 - 2012-01-01
 - 2012-01-02
 - 2012-01-03
 - 2012-01-04
 - 2013-01-01
 - 2013-01-02

date: 2013-01-02, copy: 2013-01-02
Void Stack, element size: 11, #elements: 5, max #elements: 8
Elements as strings:
 - 2012-01-01
 - 2012-01-02
 - 2012-01-03
 - 2012-01-04
 - 2013-01-01

date: 2013-01-01
date: 2013-01-01, copy: 2013-01-01
Void Stack, element size: 11, #elements: 4, max #elements: 8
Elements as strings:
 - 2012-01-01
 - 2012-01-02
 - 2012-01-03
 - 2012-01-04

==748== 
==748== HEAP SUMMARY:
==748==     in use at exit: 0 bytes in 0 blocks
==748==   total heap usage: 3 allocs, 3 frees, 1,156 bytes allocated
==748== 
==748== All heap blocks were freed -- no leaks are possible
==748== 
==748== For counts of detected and suppressed errors, rerun with: -v
==748== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

但是,如果我更改要推送的数据

    void_stack_print(&stack);

    void_stack_push(&stack, "2013-01-01XXXXXXX");  // <--- see here
    void_stack_push(&stack, "2013-01-02");

    void_stack_print(&stack);

    char date[11], copy[11];

    void_stack_pop(&stack, date);
    strcpy(copy, date);
    printf("date: %s, copy: %s\n", date, copy);
    void_stack_print(&stack);

    void_stack_pop(&stack, date);
    strcpy(copy, date);
    printf("date: %s, copy: %s\n", date, copy);
    void_stack_print(&stack);

看看会发生什么:

date: 2013-01-02, copy: 2013-01-02
Void Stack, element size: 11, #elements: 5, max #elements: 8
Elements as strings:
 - 2012-01-01
 - 2012-01-02
 - 2012-01-03
 - 2012-01-04
 - 2013-01-01X

==813== Invalid write of size 1
==813==    at 0x4C2E1D8: strcpy (vg_replace_strmem.c:510)
==813==    by 0x108C50: main (a.c:92)
==813==  Address 0x1fff001000 is not stack'd, malloc'd or (recently) free'd
==813== 
==813== 
==813== Process terminating with default action of signal 11 (SIGSEGV)
==813==  Access not within mapped region at address 0x1FFF001000
==813==    at 0x4C2E1D8: strcpy (vg_replace_strmem.c:510)
==813==    by 0x108C50: main (a.c:92)
==813==  If you believe this happened as a result of a stack
==813==  overflow in your program's main thread (unlikely but
==813==  possible), you can try to increase the size of the
==813==  main thread stack using the --main-stacksize= flag.
==813==  The main thread stack size used in this run was 8388608.
==813== 
==813== HEAP SUMMARY:
==813==     in use at exit: 88 bytes in 1 blocks
==813==   total heap usage: 3 allocs, 2 frees, 1,156 bytes allocated
==813== 
==813== LEAK SUMMARY:
==813==    definitely lost: 0 bytes in 0 blocks
==813==    indirectly lost: 0 bytes in 0 blocks
==813==      possibly lost: 0 bytes in 0 blocks
==813==    still reachable: 88 bytes in 1 blocks
==813==         suppressed: 0 bytes in 0 blocks
==813== Rerun with --leak-check=full to see details of leaked memory
==813== 
==813== For counts of detected and suppressed errors, rerun with: -v
==813== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault

我遇到了与您的错误非常相似的错误,但我认为这可以解决问题。 您的 pushpop 方法没有错误,它们复制了正确数量的 字节。问题是 "2013-01-01XXXXXXXX" 超过 11 个字符, push 复制 11 个字符但没有 '\0' 终止字节 在这 11 个字节中。 strcpy 但是需要有效的 '\0'-terminating 字符串,但如果找不到,则错误。

我在 cmets 告诉过你

如果是字符串(如weight),您是否考虑使用\0 终止字节? 真的所有字符串的长度都相同吗(您的堆栈希望输入始终具有相同的大小)?

我并没有那么远,一个问题可能是DATE_LENGTH 不够大,或者 出于某种原因,您推送的字符串长度超过 11 个字符。无论如何,你需要 确保您不会推送任何大于 10 个字符的字符串。你的堆栈不知道 字符串和字符串长度,无论类型如何,它总是复制element_size字节, 因此,push 方法的调用者有责任确保 没有字符串长度超过 10 个字符。

在执行初始推送之前使用调试器或添加 printfs 并检查每个 您推送的字符串不超过 10 个字符。

【讨论】:

  • 非常感谢您的精彩回答!正如你所建议的,我将尝试调试或printf 来检查推送。
  • 终于解决了!问题是还有 \r 字符没有从行尾修剪,因此字符串长度超过 10 个字符。非常感谢您的帮助。现在所有 Valgrind 错误都消失了。
猜你喜欢
  • 2016-04-23
  • 2015-06-22
  • 1970-01-01
  • 1970-01-01
  • 2018-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多