Bug Summary

File:.build-ci/../src/nvme-models.c
Warning:line 286, column 9
File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior

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 nvme-models.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 nvme.p -I . -I .. -I src -I ../src -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I shared -I ../shared -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -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=gnu99 -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-08-08-045408-589-1 -x c ../src/nvme-models.c
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2#include <errno(*__errno_location ()).h>
3#include <fcntl.h>
4#include <stdbool.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9
10#include <nvme/lib-types.h>
11
12#include "nvme.h"
13#include "nvme-models.h"
14#include "nvme-pci-ids.h"
15#include "nvme-print.h"
16
17#define LINE_BUF_SIZE1024 1024
18
19static char *device_top;
20static char *device_mid;
21static char *device_final;
22static char *class_top;
23static char *class_mid;
24static char *class_final;
25
26
27static void free_all(void)
28{
29 free(device_top);
30 device_top = NULL((void*)0);
31 free(device_mid);
32 device_mid = NULL((void*)0);
33 free(device_final);
34 device_final = NULL((void*)0);
35 free(class_top);
36 class_top = NULL((void*)0);
37 free(class_mid);
38 class_mid = NULL((void*)0);
39 free(class_final);
40 class_final = NULL((void*)0);
41}
42
43static char *find_data(char *data)
44{
45 while (*data != '\0') {
46 if (*data >= '0' && *data <= '9')
47 return data;
48 data++;
49 }
50 return NULL((void*)0);
51}
52
53static char *locate_info(char *data, bool_Bool is_inner, bool_Bool is_class)
54{
55 char *orig = data;
56 char *locate;
57 if (!data)
58 return orig;
59
60 locate = find_data(data);
61 if (!locate)
62 return orig;
63 if (is_class)
64 return locate + 4;
65 if (!is_inner)
66 /* 4 to get over the number, 2 for spaces */
67 return locate + 4 + 2;
68
69 /* Inner data, has "sub_ven(space)sub_dev(space)(space)string */
70 return locate + 4 + 1 + 4 + 2;
71}
72
73static void format_and_print(char *save)
74{
75
76 if (!class_mid) {
77 if (device_final)
78 snprintf(save, LINE_BUF_SIZE1024, "%s %s %s",
79 locate_info(device_top, false0, false0),
80 locate_info(device_mid, false0, false0),
81 locate_info(device_final, true1, false0));
82 else
83 snprintf(save, LINE_BUF_SIZE1024, "%s %s",
84 locate_info(device_top, false0, false0),
85 locate_info(device_mid, false0, false0));
86 } else {
87 if (device_final)
88 snprintf(save, LINE_BUF_SIZE1024, "%s: %s %s %s",
89 locate_info(class_mid, false0, true1),
90 locate_info(device_top, false0, false0),
91 locate_info(device_mid, false0, false0),
92 locate_info(device_final, true1, false0));
93 else
94 snprintf(save, LINE_BUF_SIZE1024, "%s: %s %s",
95 locate_info(class_mid, false0, true1),
96 locate_info(device_top, false0, false0),
97 locate_info(device_mid, false0, false0));
98 }
99}
100
101static void format_all(char *save, char *vendor, char *device)
102{
103 if (device_top && device_mid)
104 format_and_print(save);
105
106 else if (device_top && !device_mid && class_mid)
107 snprintf(save, LINE_BUF_SIZE1024, "%s: %s Device %s",
108 locate_info(class_mid, false0, true1),
109 locate_info(device_top, false0, false0),
110 device);
111
112 else if (!device_top && class_mid)
113 snprintf(save, LINE_BUF_SIZE1024, "%s: Vendor %s Device %s",
114 locate_info(class_mid, false0, true1),
115 vendor,
116 device);
117 else
118 snprintf(save, LINE_BUF_SIZE1024, "Unknown device");
119}
120
121static int is_final_match(char *line, char *search)
122{
123 return !memcmp(&line[2], search, 2);
124}
125
126static int is_inner_sub_vendev(char *line, char *search, char *search2)
127{
128 char combine[10];
129 snprintf(combine, sizeof(combine), "%s %s", &search[2], &search2[2]);
130 if (line[0] != '\t' && line[1] != '\t')
131 return 0;
132
133 return !memcmp(combine, &line[2], 9);
134}
135
136static int is_mid_level_match(char *line, char *device, bool_Bool class)
137{
138 if (!class)
139 return !memcmp(&line[1], &device[2], 4);
140
141 return !memcmp(&line[1], device, 2);
142}
143
144static inline bool_Bool is_comment(char *line)
145{
146 return line[0] == '#';
147}
148
149static int is_top_level_match(char *line, const char* device, bool_Bool class)
150{
151 if (line[0] == '\t')
152 return false0;
153 if (line[0] == '#')
154 return false0;
155 if (!class)
156 return !memcmp(line, &device[2], 4);
157 if (line[0] != 'C')
158 return false0;
159 /* Skipping C(SPACE) 0x */
160 return !memcmp(&line[2], &device[2], 2);
161}
162
163static inline int is_tab(char *line)
164{
165 return line[0] == '\t';
166}
167
168static inline int is_class_info(char *line)
169{
170 return !memcmp(line, "# C class", 9);
171}
172
173static void parse_vendor_device(char *line, FILE *file,
174 char *device, char *subdev,
175 char *subven)
176{
177 bool_Bool device_single_found = false0;
178 size_t len;
179
180 while (fgets(line, LINE_BUF_SIZE1024, file) != NULL((void*)0)) {
181 len = strlen(line);
182 if (len > 0 && line[len - 1] == '\n')
183 line[len - 1] = '\0';
184 if (is_comment(line))
185 continue;
186 if (!is_tab(line))
187 return;
188
189 if (!device_single_found && is_mid_level_match(line, device, false0)) {
190 device_single_found = true1;
191 device_mid = strdup(line);
192 continue;
193 }
194
195 if (device_single_found && is_inner_sub_vendev(line, subven, subdev)) {
196 device_final = strdup(line);
197 break;
198 }
199 }
200}
201
202static void pull_class_info(char *line, FILE *file, char *class)
203{
204 bool_Bool top_found = false0;
205 bool_Bool mid_found = false0;
206 size_t len;
207
208 while (fgets(line, LINE_BUF_SIZE1024, file) != NULL((void*)0)) {
10
Assuming this stream operation fails
11
Loop condition is false. Execution continues on line 302
209 len = strlen(line);
210 if (len > 0 && line[len - 1] == '\n')
211 line[len - 1] = '\0';
212 if (!top_found && is_top_level_match(line, class, true1)) {
213 class_top = strdup(line);
214 top_found = true1;
215 continue;
216 }
217 if (!mid_found && top_found &&
218 is_mid_level_match(line, &class[4], true1)) {
219 class_mid = strdup(line);
220 mid_found = true1;
221 continue;
222 }
223 if (top_found && mid_found &&
224 is_final_match(line, &class[6])) {
225 class_final = strdup(line);
226 break;
227 }
228 }
229}
230
231static FILE *open_pci_ids(void)
232{
233 int i;
234 char *pci_ids_path;
235 FILE *fp;
236
237 const char* pci_ids[] = {
238 "/usr/share/hwdata/pci.ids", /* RHEL */
239 "/usr/share/pci.ids", /* SLES */
240 "/usr/share/misc/pci.ids", /* Ubuntu */
241 NULL((void*)0)
242 };
243
244 /* First check if user gave pci ids in environment */
245 if ((pci_ids_path = getenv("PCI_IDS_PATH")) != NULL((void*)0)) {
246 if ((fp = fopen(pci_ids_path, "r")) != NULL((void*)0)) {
247 return fp;
248 } else {
249 /* fail if user provided environment variable but could not open */
250 perror(pci_ids_path);
251 return NULL((void*)0);
252 }
253 }
254
255 /* NO environment, check in predefined places */
256 for (i = 0; pci_ids[i] != NULL((void*)0); i++) {
257 if ((fp = fopen(pci_ids[i], "r")) != NULL((void*)0))
258 return fp;
259 }
260
261 return NULL((void*)0);
262}
263
264static char *__nvme_product_name(__u32 *vid, __u32 *did,
265 __u32 *subsys_vid, __u32 *subsys_did, __u32 *class_code)
266{
267 char readbuf[LINE_BUF_SIZE1024];
268 char vendor[7] = { 0 };
269 char device[7] = { 0 };
270 char sub_device[7] = { 0 };
271 char sub_vendor[7] = { 0 };
272 char class[9] = { 0 };
273 size_t len;
274 char *result;
275 FILE *file = open_pci_ids();
276
277 if (!file
3.1
'file' is non-null
)
4
Taking false branch
278 goto error1;
279
280 sprintf(vendor, "0x%04x", *vid & 0xFFFF);
281 sprintf(device, "0x%04x", *did & 0xFFFF);
282 sprintf(sub_vendor, "0x%04x", *subsys_vid & 0xFFFF);
283 sprintf(sub_device, "0x%04x", *subsys_did & 0xFFFF);
284 sprintf(class, "0x%06x", *class_code & 0xFFFFFF);
285
286 while (fgets(readbuf, sizeof(readbuf), file) != NULL((void*)0)) {
5
Loop condition is true. Entering loop body
13
File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior
287 len = strlen(readbuf);
288 if (len > 0 && readbuf[len - 1] == '\n')
6
Assuming 'len' is <= 0
289 readbuf[len - 1] = '\0';
290 if (is_comment(readbuf) && !is_class_info(readbuf))
291 continue;
292 if (is_top_level_match(readbuf, vendor, false0)) {
7
Taking false branch
293 free(device_top);
294 device_top = strdup(readbuf);
295 parse_vendor_device(readbuf, file,
296 device,
297 sub_device,
298 sub_vendor);
299 clearerr(file);
300 }
301 if (is_class_info(readbuf)) {
8
Taking true branch
302 pull_class_info(readbuf, file, class);
9
Calling 'pull_class_info'
12
Returning from 'pull_class_info'
303 clearerr(file);
304 }
305 }
306 fclose(file);
307
308 result = malloc(LINE_BUF_SIZE1024);
309 if (!result) {
310 nvme_show_error("malloc: %s", libnvme_strerror(errno))nvme_show_message(1, "malloc: %s", libnvme_strerror((*__errno_location
())))
;
311 free_all();
312 return NULL((void*)0);
313 }
314 format_all(result, vendor, device);
315 free_all();
316 return result;
317error1:
318 return NULL((void*)0);
319}
320
321char *nvme_product_name(struct libnvme_global_ctx *ctx,
322 struct libnvme_transport_handle *hdl)
323{
324 __u32 vid = 0, did = 0, subsys_vid = 0, subsys_did = 0, class_code = 0;
325
326 if (nvme_get_pci_ids(ctx, hdl, &vid, &did, &subsys_vid,
1
Assuming the condition is false
2
Taking false branch
327 &subsys_did, &class_code) < 0)
328 return NULL((void*)0);
329
330 return __nvme_product_name(&vid, &did, &subsys_vid, &subsys_did,
3
Calling '__nvme_product_name'
331 &class_code);
332}