簡體   English   中英

valgrind報告內存丟失並且大小為8的無效讀取

[英]valgrind reporting memory lost and Invalid read of size 8

我寫了這個簡單的小測試應用程序,但是valgrind報告我正在丟失內存。

您可以從字面上復制並粘貼它,並使用valgrind運行它。 也許經驗豐富的C程序員可以立即看到內存泄漏,但是我看不到。 我將發布一次運行的輸出。 如果我注釋掉vert_layout_new的for循環中的大多數代碼,則大多數錯誤都會消失。

為什么我確實會泄漏這么多內存並導致無效讀取?

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

typedef enum { POSITION = 0, COLOR, NORMAL, TEXCOORD0 } vert_attrib_type;

void vert_attrib_type_print(vert_attrib_type type);

typedef struct {
    vert_attrib_type type;
    size_t count;
    size_t attrib_size;
} vert_attrib;

typedef struct {
    vert_attrib* attributes;
    size_t count;
    size_t vert_size;
} vert_layout;

vert_attrib* vert_attrib_new(vert_attrib_type type, size_t count) {
    vert_attrib* out = calloc(1, sizeof(vert_attrib));
    out->type = type;
    out->count = count;
    out->attrib_size = out->count * sizeof(float);
    return out;
}

void vert_attrib_print(vert_attrib* a) { vert_attrib_type_print(a->type); }

vert_layout* vert_layout_new(vert_attrib* attributes, size_t count) {
    vert_layout* out = calloc(1, sizeof(vert_layout));
    out->attributes = calloc(1, sizeof(vert_attrib) * count);

    for (size_t i = 0; i < count; i++) {
        out->attributes[i] = attributes[i];
        out->vert_size += attributes[i].attrib_size;
    }

    return out;
}

int main(int argc, char* argv[]) {
    printf("argc:%s\n", argv[argc - 1]);

    vert_attrib** attribs = calloc(1, sizeof(vert_attrib) * 3);

    attribs[0] = vert_attrib_new(POSITION, 3);
    attribs[1] = vert_attrib_new(COLOR, 4);
    attribs[2] = vert_attrib_new(TEXCOORD0, 2);

    for (size_t i = 0; i < 3; i++) {
        vert_attrib_print(attribs[i]);
    }

    vert_layout* l = vert_layout_new(*attribs, 4);

    free(attribs);
    free(l->attributes);
    free(l);

    return 0;
}

void vert_attrib_type_print(vert_attrib_type t) {
    switch (t) {
        case POSITION:
            printf("Position\n");
            break;
        case COLOR:
            printf("Color\n");
            break;
        case NORMAL:
            printf("Normal\n");
            break;
        case TEXCOORD0:
            printf("TexCoord 0\n");
            break;
    }
}

這是運行該程序的輸出。

valgrind --leak-check=full -v /opt/./main
==12908== Memcheck, a memory error detector
==12908== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==12908== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==12908== Command: /opt/./main
==12908==
--12908-- Valgrind options:
--12908--    --leak-check=full
--12908--    -v
--12908-- Contents of /proc/version:
--12908--   Linux version 4.2.0-0.bpo.1-amd64 (debian-kernel@lists.debian.org) (gcc version 4.9.2 (Debian 4.9.2-10) ) #1 SMP Debian 4.2.6-3~bpo8+2 (2015-12-14)
--12908--
--12908-- Arch and hwcaps: AMD64, LittleEndian, amd64-cx16-lzcnt-rdtscp-sse3-avx-avx2-bmi
--12908-- Page sizes: currently 4096, max supported 4096
--12908-- Valgrind library directory: /opt/valgrind/lib/valgrind
--12908-- Reading syms from /opt/main
--12908-- Reading syms from /lib/x86_64-linux-gnu/ld-2.19.so
--12908--   Considering /lib/x86_64-linux-gnu/ld-2.19.so ..
--12908--   .. CRC mismatch (computed 65ef7209 wanted 26b223d2)
--12908--   Considering /usr/lib/debug/lib/x86_64-linux-gnu/ld-2.19.so ..
--12908--   .. CRC is valid
--12908-- Reading syms from /opt/valgrind/lib/valgrind/memcheck-amd64-linux
--12908--    object doesn't have a dynamic symbol table
--12908-- Scheduler: using generic scheduler lock implementation.
--12908-- Reading suppressions file: /opt/valgrind/lib/valgrind/default.supp
==12908== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-12908-by-blubee-on-???
==12908== embedded gdbserver: writing to   /tmp/vgdb-pipe-to-vgdb-from-12908-by-blubee-on-???
==12908== embedded gdbserver: shared mem   /tmp/vgdb-pipe-shared-mem-vgdb-12908-by-blubee-on-???
==12908==
==12908== TO CONTROL THIS PROCESS USING vgdb (which you probably
==12908== don't want to do, unless you know exactly what you're doing,
==12908== or are doing some strange experiment):
==12908==   /opt/valgrind/lib/valgrind/../../bin/vgdb --pid=12908 ...command...
==12908==
==12908== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==12908==   /path/to/gdb /opt/./main
==12908== and then give GDB the following command
==12908==   target remote | /opt/valgrind/lib/valgrind/../../bin/vgdb --pid=12908
==12908== --pid is optional if only one valgrind process is running
==12908==
--12908-- REDIR: 0x4017950 (ld-linux-x86-64.so.2:strlen) redirected to 0x3809ea41 (vgPlain_amd64_linux_REDIR_FOR_strlen)
--12908-- Reading syms from /opt/valgrind/lib/valgrind/vgpreload_core-amd64-linux.so
--12908-- Reading syms from /opt/valgrind/lib/valgrind/vgpreload_memcheck-amd64-linux.so
==12908== WARNING: new redirection conflicts with existing -- ignoring it
--12908--     old: 0x04017950 (strlen              ) R-> (0000.0) 0x3809ea41 vgPlain_amd64_linux_REDIR_FOR_strlen
--12908--     new: 0x04017950 (strlen              ) R-> (2007.0) 0x04c2ac80 strlen
--12908-- REDIR: 0x4017700 (ld-linux-x86-64.so.2:index) redirected to 0x4c2a830 (index)
--12908-- REDIR: 0x4017920 (ld-linux-x86-64.so.2:strcmp) redirected to 0x4c2bd30 (strcmp)
--12908-- REDIR: 0x4018650 (ld-linux-x86-64.so.2:mempcpy) redirected to 0x4c2ee30 (mempcpy)
--12908-- Reading syms from /lib/x86_64-linux-gnu/libc-2.19.so
--12908--   Considering /lib/x86_64-linux-gnu/libc-2.19.so ..
--12908--   .. CRC mismatch (computed e5556398 wanted 5318f0bf)
--12908--   Considering /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.19.so ..
--12908--   .. CRC is valid
--12908-- REDIR: 0x4eb8e10 (libc.so.6:strcasecmp) redirected to 0x4a236fe (_vgnU_ifunc_wrapper)
--12908-- REDIR: 0x4ebb100 (libc.so.6:strncasecmp) redirected to 0x4a236fe (_vgnU_ifunc_wrapper)
--12908-- REDIR: 0x4eb85e0 (libc.so.6:memcpy@GLIBC_2.2.5) redirected to 0x4a236fe (_vgnU_ifunc_wrapper)
--12908-- REDIR: 0x4eb6960 (libc.so.6:rindex) redirected to 0x4c2a510 (rindex)
--12908-- REDIR: 0x4ebf7a0 (libc.so.6:strchrnul) redirected to 0x4c2e960 (strchrnul)
--12908-- REDIR: 0x4eb87c0 (libc.so.6:__GI_mempcpy) redirected to 0x4c2eb60 (__GI_mempcpy)
argc:/opt/./main
--12908-- REDIR: 0x4eaf980 (libc.so.6:calloc) redirected to 0x4c298f0 (calloc)
--12908-- REDIR: 0x4eb4c60 (libc.so.6:strlen) redirected to 0x4c2abc0 (strlen)
Position
Color
TexCoord 0
==12908== Invalid read of size 8
==12908==    at 0x4006D5: vert_layout_new (in /opt/main)
==12908==    by 0x400817: main (in /opt/main)
==12908==  Address 0x51dc0e8 is 0 bytes after a block of size 24 alloc'd
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x400791: main (in /opt/main)
==12908==
==12908== Invalid read of size 8
==12908==    at 0x4006DB: vert_layout_new (in /opt/main)
==12908==    by 0x400817: main (in /opt/main)
==12908==  Address 0x51dc0f0 is 8 bytes after a block of size 24 alloc'd
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x400791: main (in /opt/main)
==12908==
==12908== Invalid read of size 8
==12908==    at 0x4006E3: vert_layout_new (in /opt/main)
==12908==    by 0x400817: main (in /opt/main)
==12908==  Address 0x51dc0f8 is 16 bytes after a block of size 24 alloc'd
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x400791: main (in /opt/main)
==12908==
==12908== Invalid read of size 8
==12908==    at 0x40070E: vert_layout_new (in /opt/main)
==12908==    by 0x400817: main (in /opt/main)
==12908==  Address 0x51dc0f8 is 16 bytes after a block of size 24 alloc'd
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x400791: main (in /opt/main)
==12908==
--12908-- REDIR: 0x4eaf650 (libc.so.6:free) redirected to 0x4c28ca2 (free)
==12908==
==12908== HEAP SUMMARY:
==12908==     in use at exit: 72 bytes in 3 blocks
==12908==   total heap usage: 6 allocs, 3 frees, 264 bytes allocated
==12908==
==12908== Searching for pointers to 3 not-freed blocks
==12908== Checked 66,184 bytes
==12908==
==12908== 24 bytes in 1 blocks are definitely lost in loss record 1 of 3
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x400791: main (in /opt/main)
==12908==
==12908== 24 bytes in 1 blocks are definitely lost in loss record 2 of 3
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x4007B2: main (in /opt/main)
==12908==
==12908== 24 bytes in 1 blocks are definitely lost in loss record 3 of 3
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x4007CC: main (in /opt/main)
==12908==
==12908== LEAK SUMMARY:
==12908==    definitely lost: 72 bytes in 3 blocks
==12908==    indirectly lost: 0 bytes in 0 blocks
==12908==      possibly lost: 0 bytes in 0 blocks
==12908==    still reachable: 0 bytes in 0 blocks
==12908==         suppressed: 0 bytes in 0 blocks
==12908==
==12908== ERROR SUMMARY: 15 errors from 7 contexts (suppressed: 0 from 0)
==12908==
==12908== 3 errors in context 1 of 7:
==12908== Invalid read of size 8
==12908==    at 0x40070E: vert_layout_new (in /opt/main)
==12908==    by 0x400817: main (in /opt/main)
==12908==  Address 0x51dc0f8 is 16 bytes after a block of size 24 alloc'd
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x400791: main (in /opt/main)
==12908==
==12908==
==12908== 3 errors in context 2 of 7:
==12908== Invalid read of size 8
==12908==    at 0x4006E3: vert_layout_new (in /opt/main)
==12908==    by 0x400817: main (in /opt/main)
==12908==  Address 0x51dc0f8 is 16 bytes after a block of size 24 alloc'd
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x400791: main (in /opt/main)
==12908==
==12908==
==12908== 3 errors in context 3 of 7:
==12908== Invalid read of size 8
==12908==    at 0x4006DB: vert_layout_new (in /opt/main)
==12908==    by 0x400817: main (in /opt/main)
==12908==  Address 0x51dc0f0 is 8 bytes after a block of size 24 alloc'd
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x400791: main (in /opt/main)
==12908==
==12908==
==12908== 3 errors in context 4 of 7:
==12908== Invalid read of size 8
==12908==    at 0x4006D5: vert_layout_new (in /opt/main)
==12908==    by 0x400817: main (in /opt/main)
==12908==  Address 0x51dc0e8 is 0 bytes after a block of size 24 alloc'd
==12908==    at 0x4C29986: calloc (vg_replace_malloc.c:711)
==12908==    by 0x4005F3: vert_attrib_new (in /opt/main)
==12908==    by 0x400791: main (in /opt/main)
==12908==
==12908== ERROR SUMMARY: 15 errors from 7 contexts (suppressed: 0 from 0)

此處發生無效讀取(請參見內聯注釋):

vert_layout* vert_layout_new(vert_attrib* attributes, size_t count) {
    vert_layout* out = calloc(1, sizeof(vert_layout));
    out->attributes = calloc(1, sizeof(vert_attrib) * count);

    for (size_t i = 0; i < count; i++) {
        // attributes is a pointer to a single vert_attribute,
        // not an array
        out->attributes[i] = attributes[i];
        out->vert_size += attributes[i].attrib_size;
    }

    return out;
}

int main(int argc, char* argv[]) {
    ...
    vert_attrib** attribs = calloc(1, sizeof(vert_attrib) * 3);
    ...    
    // passing in attribs[0], which points to vert_attrib
    // also passing in size 4 instead of 3
    vert_layout* l = vert_layout_new(*attribs, 4);
    ...
}

您應該將vert_layout_new傳遞attribs vert_layout_new而不是*attribs ,並相應地更改定義。

至於內存泄漏,則免費attribs而不是單獨的數組元素,其各自獨立地分配。

修復:

vert_layout* vert_layout_new(vert_attrib** attributes, size_t count) {
    vert_layout* out = calloc(1, sizeof(vert_layout));
    out->attributes = calloc(1, sizeof(vert_attrib) * count);

    for (size_t i = 0; i < count; i++) {
        out->attributes[i] = *(attributes[i]);
        out->vert_size += attributes[i]->attrib_size;
    }

    return out;
}

int main(int argc, char* argv[]) {
    ....
    vert_layout* l = vert_layout_new(attribs, 3);
    ....
    for (size_t i = 0; i < 3; i++) {
        free(attribs[i]);
    }
    free(attribs);
    ...
}

當釋放“ l-> attributes”時,將泄漏數組插槽0、1和2中的指針。

vert_layout_new(*attribs, 4); 導致不確定的行為。 *attribsattribs[0] )是指向單個屬性的指針。 但是vert_layout_new函數然后嘗試從該內存位置讀取4個屬性。

換句話說, main分配3個單獨的分配,每個分配包含1個屬性,但是在vert_layout_new您只讀取同一分配內相鄰的屬性。

無效讀取來自您嘗試從attribs[0]指向的分配結束后再讀取3個屬性。

而且,正如Les指出的那樣,您永遠不會釋放vert_attrib_new分配的內存。


要解決此問題,您需要在兩個地方使用相同的策略:要么在main連續分配屬性(這意味着更改vert_attrib_new函數),要么使vert_layout_new接受vert_attrib ** (這也將要求vert_layout也包含vert_attrib ** ,除非您要在那時復制屬性)。

它可能會幫助您在所需的內存布局的紙上繪制圖表,並用箭頭顯示所指向的位置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM