Bug Summary

File:.build-ci/../discoverd/src/netif.c
Warning:line 42, column 2
Use of memory after it is released

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name netif.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I discoverd/nvme-discoverd.p -I discoverd -I ../discoverd -I ../discoverd/src -I ccan -I ../ccan -I . -I .. -I libnvme/src -I ../libnvme/src -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -D NVME_PATH="/usr/local/sbin/nvme" -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-10-24-125112-639-1 -x c ../discoverd/src/netif.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * This file is part of nvme-cli.
4 * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries.
5 *
6 * Authors: Martin Belanger <martin.belanger@dell.com>
7 */
8
9#include <errno(*__errno_location ()).h>
10#include <net/if.h>
11#include <stdlib.h>
12#include <systemd/sd-device.h>
13
14#include <ccan/list/list.h>
15
16#include <daemon-util/log.h>
17
18#include "netif.h"
19
20struct tracked_iface {
21 struct list_node entry;
22 int ifindex;
23};
24
25struct netif_ctx {
26 struct netif_callbacks callbacks;
27 void *user_data;
28 sd_device_monitor *monitor;
29 struct list_head tracked; // candidates reported with iface_add
30};
31
32bool_Bool netif_is_candidate(unsigned int flags)
33{
34 return (flags & IFF_UPIFF_UP) && (flags & IFF_MULTICASTIFF_MULTICAST) &&
35 !(flags & IFF_LOOPBACKIFF_LOOPBACK);
36}
37
38static struct tracked_iface *tracked_find(struct netif_ctx *nctx, int ifindex)
39{
40 struct tracked_iface *e;
41
42 list_for_each(&nctx->tracked, e, entry)for ((e) = list_node_to_off_(((void)"../discoverd/src/netif.c"
":" "42", (&nctx->tracked))->n.next, (((__builtin_offsetof
(typeof(*e), entry) + ((typeof(e->entry) *)0 != (struct list_node
*)0))))); list_node_from_off_((void *)(e), (((__builtin_offsetof
(typeof(*e), entry) + ((typeof(e->entry) *)0 != (struct list_node
*)0))))) != &((&nctx->tracked))->n; (e) = list_node_to_off_
(list_node_from_off_((void *)(e), (((__builtin_offsetof(typeof
(*e), entry) + ((typeof(e->entry) *)0 != (struct list_node
*)0)))))->next, (((__builtin_offsetof(typeof(*e), entry) +
((typeof(e->entry) *)0 != (struct list_node *)0))))))
{
37
Use of memory after it is released
43 if (e->ifindex == ifindex)
44 return e;
45 }
46
47 return NULL((void*)0);
48}
49
50static unsigned int device_flags(sd_device *dev)
51{
52 const char *val = NULL((void*)0);
53
54 sd_device_get_sysattr_value(dev, "flags", &val);
55
56 return val ? (unsigned int)strtoul(val, NULL((void*)0), 0) : 0;
57}
58
59/*
60 * Bring the tracked state of one interface in line with @candidate. This
61 * is the only place that calls iface_add and iface_remove.
62 */
63static void reconcile(struct netif_ctx *nctx, int ifindex, const char *ifname,
64 bool_Bool candidate)
65{
66 struct tracked_iface *e = tracked_find(nctx, ifindex);
36
Calling 'tracked_find'
67
68 if (candidate
22.1
'candidate' is false
&& !e) {
69 e = calloc(1, sizeof(*e));
70 if (!e) {
71 log_err("%s: out of memory", ifname)dmn_log_msg(DMN_LOG_ERR, "%s: out of memory", ifname);
72 return;
73 }
74 e->ifindex = ifindex;
75 list_add(&nctx->tracked, &e->entry)list_add_(&nctx->tracked, &e->entry, "../discoverd/src/netif.c"
":" "75")
;
76 if (nctx->callbacks.iface_add)
77 nctx->callbacks.iface_add(ifindex, ifname,
78 nctx->user_data);
79 } else if (!candidate
22.2
'candidate' is false
&& e
22.3
'e' is non-null
) {
23
Taking true branch
80 list_del(&e->entry)list_del_(&e->entry, "../discoverd/src/netif.c" ":" "80"
)
;
81 free(e);
24
Memory is released
82 if (nctx->callbacks.iface_remove)
25
Assuming field 'iface_remove' is null
26
Taking false branch
83 nctx->callbacks.iface_remove(ifindex, ifname,
84 nctx->user_data);
85 }
86}
87
88static void reconcile_device(struct netif_ctx *nctx, sd_device *dev,
89 bool_Bool removed)
90{
91 const char *ifname = NULL((void*)0);
92 int ifindex;
93
94 if (sd_device_get_sysname(dev, &ifname) < 0)
18
Assuming the condition is false
19
Taking false branch
31
Assuming the condition is false
32
Taking false branch
95 return;
96 if (sd_device_get_ifindex(dev, &ifindex) < 0)
20
Assuming the condition is false
21
Taking false branch
33
Assuming the condition is false
34
Taking false branch
97 return;
98
99 reconcile(nctx, ifindex, ifname,
22
Calling 'reconcile'
27
Returning; memory was released
35
Calling 'reconcile'
100 !removed
21.1
'removed' is false
34.1
'removed' is false
&& netif_is_candidate(device_flags(dev)))
;
101}
102
103static int net_monitor_handler(
104 sd_device_monitor *monitor __attribute__((unused)),
105 sd_device *dev, void *user_data)
106{
107 sd_device_action_t action;
108
109 if (sd_device_get_action(dev, &action) < 0)
110 return 0;
111
112 reconcile_device(user_data, dev, action == SD_DEVICE_REMOVE);
113
114 return 0;
115}
116
117static int enumerate_existing(struct netif_ctx *nctx)
118{
119 sd_device_enumerator *en = NULL((void*)0);
120 sd_device *dev;
121 int r;
122
123 r = sd_device_enumerator_new(&en);
124 if (r < 0)
12
Assuming 'r' is >= 0
13
Taking false branch
125 return r;
126 r = sd_device_enumerator_add_match_subsystem(en, "net", true1);
127 if (r < 0)
14
Assuming 'r' is >= 0
15
Taking false branch
128 goto out;
129
130 for (dev = sd_device_enumerator_get_device_first(en); dev;
16
Loop condition is true. Entering loop body
29
Loop condition is true. Entering loop body
131 dev = sd_device_enumerator_get_device_next(en))
132 reconcile_device(nctx, dev, false0);
17
Calling 'reconcile_device'
28
Returning; memory was released
30
Calling 'reconcile_device'
133out:
134 sd_device_enumerator_unref(en);
135
136 return r;
137}
138
139int netif_start(sd_event *event, const struct netif_callbacks *callbacks,
140 void *user_data, struct netif_ctx **nctxp)
141{
142 struct netif_ctx *nctx;
143 int r;
144
145 nctx = calloc(1, sizeof(*nctx));
146 if (!nctx)
1
Assuming 'nctx' is non-null
2
Taking false branch
147 return -ENOMEM12;
148
149 nctx->callbacks = *callbacks;
150 nctx->user_data = user_data;
151 list_head_init(&nctx->tracked);
152
153 r = sd_device_monitor_new(&nctx->monitor);
154 if (r < 0)
3
Assuming 'r' is >= 0
4
Taking false branch
155 goto err;
156 r = sd_device_monitor_filter_add_match_subsystem_devtype(
157 nctx->monitor, "net", NULL((void*)0));
158 if (r < 0)
5
Assuming 'r' is >= 0
6
Taking false branch
159 goto err;
160 r = sd_device_monitor_attach_event(nctx->monitor, event);
161 if (r < 0)
7
Assuming 'r' is >= 0
8
Taking false branch
162 goto err;
163 r = sd_device_monitor_start(nctx->monitor, net_monitor_handler, nctx);
164 if (r < 0)
9
Assuming 'r' is >= 0
10
Taking false branch
165 goto err;
166
167 r = enumerate_existing(nctx);
11
Calling 'enumerate_existing'
168 if (r < 0)
169 goto err;
170
171 *nctxp = nctx;
172
173 return 0;
174err:
175 netif_stop(nctx);
176
177 return r;
178}
179
180void netif_stop(struct netif_ctx *nctx)
181{
182 struct tracked_iface *e, *next;
183
184 if (!nctx)
185 return;
186
187 list_for_each_safe(&nctx->tracked, e, next, entry)for ((e) = list_node_to_off_(((void)"../discoverd/src/netif.c"
":" "187", (&nctx->tracked))->n.next, (((__builtin_offsetof
(typeof(*e), entry) + ((typeof(e->entry) *)0 != (struct list_node
*)0))))), (next) = list_node_to_off_(list_node_from_off_((e)
, (((__builtin_offsetof(typeof(*e), entry) + ((typeof(e->entry
) *)0 != (struct list_node *)0)))))->next, (((__builtin_offsetof
(typeof(*e), entry) + ((typeof(e->entry) *)0 != (struct list_node
*)0))))); list_node_from_off_((e), (((__builtin_offsetof(typeof
(*e), entry) + ((typeof(e->entry) *)0 != (struct list_node
*)0))))) != &((&nctx->tracked))->n; (e) = (next
), (next) = list_node_to_off_(list_node_from_off_((e), (((__builtin_offsetof
(typeof(*e), entry) + ((typeof(e->entry) *)0 != (struct list_node
*)0)))))->next, (((__builtin_offsetof(typeof(*e), entry) +
((typeof(e->entry) *)0 != (struct list_node *)0))))))
188 free(e);
189
190 sd_device_monitor_unref(nctx->monitor);
191 free(nctx);
192}