-
Notifications
You must be signed in to change notification settings - Fork 923
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix clang static analyzer false positive #180
base: master
Are you sure you want to change the base?
Conversation
Fixes troydhanson#128. Clang static analyzer was thinking that it's possible to free() list head without updating head pointer. This assert assures static analyzer that branch without head pointer update could be taken only when we are freeing non-head element.
Hello, |
@@ -473,6 +473,7 @@ do { | |||
(head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ | |||
} \ | |||
if (_hd_hh_del->prev != NULL) { \ | |||
assert(&(head)->hh != _hd_hh_del); \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern with this one is that it's introducing assert
(and <assert.h>
) into "uthash.h", which historically hasn't been the case. I know "utlist.h" uses <assert.h>
, but "uthash.h" itself has a lot more users than "utlist.h", so I'm leery of introducing new header dependencies (even if they are C89 standard) and especially new runtime dependencies (e.g. you're introducing a hidden dependency on the libc's abort
function, right?).
Is there any way to silence scan-build's warning here with core-language features instead of assert
?
I even wonder if we could just change line 475 from if (_hd_hh_del->prev != NULL)
to if (_hd_hh_del != &(head)->hh)
— does that work? It shouldn't really be any less efficient, since we have to load (head)->hh
on line 476 anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, I'd also be extremely interested in PRs against our .travis.yml
file so that we could actually test the scan-build build against regressions in this area. Any takers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I even wonder if we could just change line 475 from if (_hd_hh_del->prev != NULL) to if (_hd_hh_del != &(head)->hh) — does that work?
Nope, this introduced a new warning warning: Dereference of null pointer [clang-analyzer-core.NullDereference]
.
Using the assert and including <assert.h> in uthash.h solved the problem, can we go ahead with that fix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
including <assert.h> in uthash.h solved the problem, can we go ahead with that fix?
No.
Any volunteers to add scan-build into .travis.yml
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can take a stab
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I'm able to reproduce locally (thanks @chaitu-tk!) I see that we could silence the false positives on test15, test40, and test59 by adding assert((delptr) != (head))
at the end of HASH_DELETE
— i.e., "after deleting an item from the hash table, the deleted item is no longer in the table at all, let alone at the front of the table."
Except that this breaks test18, because we want to permit HASH_DEL(table, table)
to mean "delete the first item in the table." And anyway, I still don't want to add a dependency on <assert.h>
to uthash.
I've just fixed some low-hanging fruit in 45af88c and f0e1bd9 (which I'll merge up to this repo once Travis passes).
Fixes #128. Clang static analyzer was thinking that it's possible to
free() list head without updating head pointer. This assert assures
static analyzer that branch without head pointer update could be taken
only when we are freeing non-head element.