troydhanson / uthash

C macros for hash tables and more

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Check for (head)->hh.tbl in HASH_DELETE

jcraffael opened this issue · comments

Hello,

I think it would be necessary to check the validity of the pointer (head)->hh.tbl being as an argument for
HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del) inside HASH_DELETE_HH, since in my case of same item with 2 keys I got a coredump here since the (head)->hh.tbl has a trash value when trying to delete the item from the 2nd hash table.
Thank you!

Chao

Hi @jcraffael! Can you post a short, self-contained example that demonstrates the crash you're seeing? For example,

#include "utlist.h"
struct el {
    int id;
    struct el *next, *prev;
};
int main() {
    struct el *head = NULL;
    struct el x = {0};
    DL_APPEND(head, &x);
    DL_DELETE(head, &x);  // segfault on this line
}

except with whatever your steps-to-reproduce are?

Hi, thank you for the reply!

It's hard to explain but let me try:

struct el {struct key key1; struct key key2; UT_hash_handle hh1; UT_hash_handle hh2;};

int main(){
el n1, n2, s tmp;
n1 = ..., n2 = ...;
HASH_ADD(hh1, n1, ...);
HASH_ADD(hh2. n2, ...);

HASH_ITER(hh1, n1, s, tmp)
{
HASH_DELETE(hh1, n1, s);
if(...)
   HASH_DELETE(hh2, n2, s);  //segfault here since (el *)(s->hh2.next)->hh2.tbl results in a trash value
}
}

Hope that makes sense, thank you!
Chao

Hi @jcraffael , could you please fill in the ... parts with something that makes it crash? (Also, on the first line of main, I don't think e1 n1, n2, s tmp; is valid syntax.)

Hello, so sorry for the typo.
Again:

struct el {struct key key1; struct key key2; UT_hash_handle hh1; UT_hash_handle hh2;};
int main(){
struct el *n1, *n2, *s, *tmp, *p;

/* Many repeated find and add */
HASH_FIND(hh1, n1, key, size, p);
if(!p)
   HASH_ADD(hh1, n1, key, size, p);
HASH_FIND(hh2, n2, key, size, p);
if(!p)
   HASH_ADD(hh2. n2, key, size, p);

/* Many repeated iter and delete */
HASH_ITER(hh1, n1, s, tmp)
{
   HASH_DELETE(hh1, n1, s);
   if(...) {
      HASH_DELETE(hh2, n2, s);  /* here since the data members under (struct el *)(s->hh2.next)->hh2.tbl got trash value, the macro HASH_DEL_IN_BKT(hh2,(head)->hh2.tbl->buckets[_hd_bkt], _hd_hh_del) causes segfault */
   }
   free(s);
   s = NULL;
}
HASH_FIND(hh1, n1, key, size, p);
if(!p)
   HASH_ADD(hh1, n1, key, size, p);

I believe this is your problem, here. You're using HASH_FIND correctly — the fifth parameter is an "out-parameter," so uthash will fill in p with the found element's address (or null if not found). But you're using HASH_ADD incorrectly: HASH_ADD expects its fifth parameter to be the address of a new node to add into the hash. You're passing p, which is still null from the previous line. That's a null pointer dereference, which explains the segfault you're seeing.

You should be doing more like this: https://godbolt.org/z/Wc8fjsszT

HASH_FIND(hh1, n1, &k, size, p);
if (!p) {
    struct el *newel = (struct el*)malloc(sizeof *newel);
    newel->key1 = {1};
    newel->key2 = {2};
    HASH_ADD(hh1, n1, key1, size, newel);
}

Hope this helps!

commented

@Quuxplusone I think this was also a "typo" on the OP's part. Attempting to HASH_ADD a NULL will blow in HASH_ADD right away, not in some later evaluation of HASH_DELETE

Hi @veselov correct! Thank you for the help!
Hi @Quuxplusone please allow me to explain: I did HASH_ADD exactly as you showed. Just for short I didn't specify that in my code. It's not my first time using uthash so I know pretty well how to use these APIs.
This time I'm dealing with a complicated case where the same struct el got added and deleted in time. What I found in my case is firstly DECLTYPE_ASSIGN() is called, but (delptr)->hh.next at that moment should be an address already purged from hh but still present there, with its data members having trash value. Then in HASH_TO_BKT the value of (head)->hh.tbl->num_buckets which is a trash one is assigned to _hd_bkt and afterward in HASH_DEL_IN_BKT (head)->hh.tbl->buckets[_hd_bkt] results in outside of range so a coredump occurs.
image

Hope that makes sense, thank you!
Chao