簡體   English   中英

在終止的程序上找不到valgrind錯誤並正確返回正確的輸出

[英]Can't find valgrind error on program which terminates and returns the right output correctly

即使程序正確終止並給我正確的輸出,並且沒有內存泄漏,valgrind給了我一些這樣的錯誤:

Invalid read of size 1
==910==    at 0x108DD4: fnv_hash_function (user.c:24)
==910==    by 0x108E17: hash (user.c:29)
==910==    by 0x109A50: icl_hash_find (icl_hash.c:114)
==910==    by 0x1094DB: db_request (user.c:197)
==910==    by 0x108D2E: main (tuser.c:65)
==910==  Address 0x5416f50 is 0 bytes inside a block of size 15 free'd
==910==    at 0x4C2E10B: free (vg_replace_malloc.c:530)
==910==    by 0x109152: freeKey (user.c:138)
==910==    by 0x109CF2: icl_hash_delete (icl_hash.c:192)
==910==    by 0x109796: db_request (user.c:222)
==910==    by 0x108CF8: main (tuser.c:59)
==910==  Block was alloc'd at
==910==    at 0x4C2CEDF: malloc (vg_replace_malloc.c:299)
==910==    by 0x108BDC: main (tuser.c:35)

hashfnv_hash_function以這種方式定義

static inline unsigned int fnv_hash_function( void *key, int len ) {
    unsigned char *p = (unsigned char*)key;
    unsigned int h = 2166136261u;
    int i;
    for ( i = 0; i < len; i++ )
        h = ( h * 16777619 ) ^ p[i]; //this is the line 24
    return h;
}


unsigned int hash(void *key){
    return fnv_hash_function(key, strlen(key));
}

我想問題是^運算符,但我無法弄清楚問題是什么,因為程序以正確的輸出終止而沒有分段錯誤。

icl_hash_find不是我寫的函數,而是在我正在使用的庫中,並以這種方式定義

void *
icl_hash_find(icl_hash_t *ht, void* key)
{
    icl_entry_t* curr;
    unsigned int hash_val;

    if(!ht || !key) return NULL;

    hash_val = (* ht->hash_function)(key) % ht->nbuckets;

    for (curr=ht->buckets[hash_val]; curr != NULL; curr=curr->next)
        if ( ht->hash_key_compare(curr->key, key))
            return(curr->data);

    return NULL;
}

我在該庫的測試套件上嘗試了valgrind,沒有發現任何錯誤,所以我懷疑問題是否存在。

編輯:密鑰在此for循環中分配:

char * s; //string used as key
for(int i = 0; i < N; i++){

    s = (char *)malloc(NAMELEN * sizeof(char));
    sprintf(s, "Utente %d", i);
    u = create_user( s , i);

    if(!db_request(db, s, u, PUT)){
        perror("problema PUT");
        exit(EXIT_FAILURE);
    }
    .
    .
    .

編輯2:這是db_request的主體:

bool db_request(userbase_t *db, char * key, user_t * u, dbop_t op ){

if(db==NULL || key == NULL ||(op!=DELETE && u==NULL)){
    errno = EINVAL;
    return false;
}
int lock_index; //indice del lock del bucket
switch(op){
    //implementazione PUT
    case PUT : 
        lock_index = db -> table -> hash_function(key) % db->nlocks;
        WLOCK(&db->locks[lock_index])
        errno = 0;
        if(icl_hash_insert(db->table, key, (void *) u)==NULL){
            RWUNLOCK(&db->locks[lock_index])
            //la chiave e' gia' associata ad un utente
            if(errno == EINVAL){
                perror("key gia' presente");
            }
            return false;
        }
        RWUNLOCK(&db->locks[lock_index])
        return true;
    //implementazione GET
    case GET :
        lock_index = db -> table -> hash_function(key) % db->nlocks;
        RLOCK(&db->locks[lock_index])

        u = icl_hash_find(db->table, (void *)key );

        RWUNLOCK(&db->locks[lock_index]);
        return true;
    //implementazione update
    case UPDATE :
        //elimina il vecchio e aggiunge il nuovo
        lock_index = db -> table -> hash_function(key) % db->nlocks;
        WLOCK(&db->locks[lock_index]);

        if(icl_hash_delete(db->table, key, freeKey, freeUser)){
            perror("problema UPDATE (icl_hash_delete) ");
            RWUNLOCK(&db->locks[lock_index]);
            return false;
        }

        if (icl_hash_insert(db->table, key, (void *) u)==NULL){
            perror("problema UPDATE (icl_hash_insert)");
            RWUNLOCK(&db->locks[lock_index]);
            return false;
        }
    case DELETE :
        lock_index = db -> table -> hash_function(key) % db->nlocks;
        WLOCK(&db->locks[lock_index]);          

        if(icl_hash_delete(db->table, key, freeKey, freeUser)){
            perror("problema DELETE");
            RWUNLOCK(&db->locks[lock_index]);
            return false;
        }

        RWUNLOCK(&db->locks[lock_index]);
        return true;
    //mai raggiunto
    default :
        errno = EINVAL;
        perror("problema switch op");
        return false;

}}

但我找不到問題,我開始認為問題出在icl_hash lib中。

當我在測試函數中刪除的元素上調用GET時會發生問題。

if(!db_request(db, s , u ,DELETE)){
            perror("problema DELETE");
            exit(EXIT_FAILURE);
        };

//provo a ottenerlo di nuovo
//The error happens here
if(!db_request(db, s , u ,GET)){
    perror("GET");
    exit(EXIT_FAILURE);
};

get唯一能做的就是調用這個函數:

void *
icl_hash_find(icl_hash_t *ht, void* key)
{
icl_entry_t* curr;
unsigned int hash_val;

if(!ht || !key) return NULL;

hash_val = (* ht->hash_function)(key) % ht->nbuckets;

for (curr=ht->buckets[hash_val]; curr != NULL; curr=curr->next)
    if ( ht->hash_key_compare(curr->key, key))
        return(curr->data);

return NULL;
}

您的無效訪問權限是已釋放的內存:

 Invalid read of size 1 ==910== at 0x108DD4: fnv_hash_function (user.c:24) ==910== by 0x108E17: hash (user.c:29) ==910== by 0x109A50: icl_hash_find (icl_hash.c:114) ==910== by 0x1094DB: db_request (user.c:197) ==910== by 0x108D2E: main (tuser.c:65) ==910== Address 0x5416f50 is 0 bytes inside a block of size 15 free'd ==910== at 0x4C2E10B: free (vg_replace_malloc.c:530) ==910== by 0x109152: freeKey (user.c:138) ==910== by 0x109CF2: icl_hash_delete (icl_hash.c:192) ==910== by 0x109796: db_request (user.c:222) ==910== by 0x108CF8: main (tuser.c:59) ==910== Block was alloc'd at ==910== at 0x4C2CEDF: malloc (vg_replace_malloc.c:299) ==910== by 0x108BDC: main (tuser.c:35) 

該塊在文件user.c中的函數db_request()中的第222行調用icl_hash_delete()被釋放。 無效的訪問是在一個呼叫作出icl_hash_find()在行197 db_request()user.c 你說icl_hash*代碼是提供給你的。

如果沒有函數體, db_request()很難確定發生了什么,但至少有幾種可能性。

  1. 你有一個懸掛指針指向被刪除的條目,你不應該仍然使用它。
  2. 刪除哈希記錄后, icl_hash*函數中的代碼處理錯誤的數據。

如果icl_hash.c函數的提供者相當可靠,那么假設問題出在db_request()代碼中是明智的。 特別注意第197行和第222行,並將變量(指針)傳遞給icl_hash_find()icl_hash_delete()函數。 再次查看這些功能的手冊頁,了解規則是什么。

如果您不確定icl_hash.c代碼的質量,您應該創建一個較小的MCVE,創建一個哈希表,添加一些行,查找一些行,刪除一些條目,並進行更多查找。 這將幫助您確定代碼或icl_hash.c代碼中是否存在問題。

由於內存是在main()而不是db_request()中分配的,因此您可能必須查看您在該級別執行的操作,但我的猜測是您將該指針傳遞給db_request()並將其所有權交給哈希當你添加一個條目的表時, icl_hash_delete()的規范說它會釋放你傳遞給哈希表的內存,並且你不小心仍然拿着一個指向現在釋放的內存的指針( db_request()函數的參數) 。 你必須非常小心,以確保你知道誰擁有什么內存 - 以及何時釋放內存。

Valgrind告訴你fnv_hash_function函數有問題。 在此函數中,您可以在freeKey釋放(取消分配)后訪問內存。 它甚至告訴您,您可以在該內存塊的開頭准確訪問它。 你在main (tuser.c:35)分配了這個內存main (tuser.c:35) 你的程序是偶然的。 只要有問題的內存塊不會在內存頁面的開頭結束並且它將是該頁面上釋放的最后一個塊 - 它將繼續工作 - 那么頁面將是(可能 - 取決於分配器策略)從您的進程空間中取消映射。 如果您在訪問它之后取消fnv_hash_function (如在fnv_hash_function ),您的程序將在您嘗試訪問它時完全崩潰。

當該塊最終成為頁面上的第一個塊並成為最后一個被釋放的塊時? - 很難說在對源代碼或底層庫(包括您無法控制的系統庫)進行任何更改后可能會發生這種情況。

暫無
暫無

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

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