[clang][analyzer] False positive core.NullDereference after updating list head through pointer-to-pointer backlink
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
**Description:**
The analyzer reports a null-pointer dereference on an impossible second loop iteration. The non-returning path in `reproducer()` expects a valid one-element list (`head -> [node] -> NULL`), and when updated inside `unlink_node()`, line `*node->previous = node->next;` is equivalent to `*(&head) = NULL`, so `head` becomes a `NULL` and the loop body execute only once.
**Reproducer:**
```c
#define NULL ((void*)0)
void clang_analyzer_eval(int);
void clang_analyzer_numTimesReached(void);
typedef struct Node Node;
struct Node {
Node *next;
Node **previous;
};
static Node *head;
void unlink_node(Node *node)
{
*node->previous = node->next;
if (node->next != NULL)
node->next->previous = node->previous;
node->previous = NULL;
node->next = NULL;
}
void reproducer(void)
{
if (head == NULL || head->next != NULL || head->previous != &head)
return;
clang_analyzer_eval(head->previous == &head);
while (head != 0) {
clang_analyzer_numTimesReached();
Node *node = head;
unlink_node(node);
clang_analyzer_eval(head == NULL);
}
}
```
**Output:**
```sh
mre.c:18:21: warning: Dereference of null pointer (loaded from field 'previous') [core.NullDereference]
18 | *node->previous = node->next;
| ~~~~~~~~ ^
mre.c:32:5: warning: UNKNOWN [debug.ExprInspection]
32 | clang_analyzer_eval(head->previous == &head);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mre.c:35:2: warning: 2 [debug.ExprInspection]
35 | clang_analyzer_numTimesReached();
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mre.c:40:2: warning: FALSE [debug.ExprInspection]
40 | clang_analyzer_eval(head == NULL);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
However, the analyzer:
- reports UNKNOWN for `head->previous == &head` (should be TRUE);
- reports FALSE for `head == NULL` after unlinking (should be TRUE);
- reaches the loop body twice (should be only once);
- reports a null dereference during the second iteration.
**Clang version:**
```sh
clang version 24.0.0git (https://github.com/llvm/llvm-project.git d71427f70210606cc41a460b4a1101b99e5f882f)
Target: x86_64-unknown-linux-gnu
Thread model: posix
Build config: +assertions
```
Contributor guide
Research direction
Start by running the supplied reproducer() with the stated Clang version and compare the analyzer output with the expected pointer updates. Trace unlink_node(), especially the pointer-to-pointer assignment and the calls to clang_analyzer_eval() and clang_analyzer_numTimesReached(); done means the assertions report TRUE, the loop reaches once, and no null-dereference warning is emitted.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100