Bug Summary

File:.build-ci/../tests/unit/c/argconfig-getopt-ref.c
Warning:line 101, column 8
Dereference of null pointer (loaded from variable 'arg')

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 argconfig-getopt-ref.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 tests/unit/c/test-argconfig-diff.p -I tests/unit/c -I ../tests/unit/c -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -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=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 ../tests/unit/c/argconfig-getopt-ref.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * A frozen copy of the getopt based argconfig parser, as it was before
4 * argconfig got its own parser. test-argconfig-diff compares both, so any
5 * change in behaviour shows up. Help output and the locale side effect
6 * are left out. The test compares the parsing of the command line, so the
7 * value conversion is the one of argconfig, which uses the strict number
8 * parsers of shared/parse-util.
9 */
10
11#include <ctype.h>
12#include <errno(*__errno_location ()).h>
13#include <limits.h>
14#include <getopt.h>
15#include <stdbool.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20#include <libnvme.h>
21
22#include <shared/parse-util.h>
23#include <shared/suffix-util.h>
24
25#include "../../../src/argconfig.h"
26#include "../../../src/cleanup.h"
27#include "argconfig-getopt-ref.h"
28
29/* getopt's has_arg for an option */
30static int has_arg(struct argconfig_commandline_options *s)
31{
32 switch (s->argument_type) {
33 case ARGCONFIG_ARG_REQUIRED:
34 return required_argument1;
35 case ARGCONFIG_ARG_OPTIONAL:
36 return optional_argument2;
37 default:
38 return no_argument0;
39 }
40}
41
42static bool_Bool is_null_or_empty(const char *s)
43{
44 return !s || !*s;
45}
46
47/* The value conversion of argconfig, see argconfig_parse_type() */
48static int argconfig_parse_type(struct argconfig_commandline_options *s,
49 char *arg)
50{
51 void *value = s->default_value;
52 char *endptr;
53 uint64_t u;
54 int64_t i;
55 double d;
56 int ret = 0;
57
58 if (!arg && s->config_type != CFG_STRING &&
23
Assuming 'arg' is null
24
Assuming field 'config_type' is equal to CFG_STRING
59 s->config_type != CFG_FLAG && s->config_type != CFG_INCREMENT)
60 return -EINVAL22;
61
62 switch (s->config_type) {
25
Control jumps to 'case CFG_LONG_SUFFIX:' at line 100
63 case CFG_STRING:
64 case CFG_FILE:
65 case CFG_DIR:
66 *(char **)value = arg;
67 break;
68 case CFG_INT:
69 ret = shr_parse_s64(arg, INT_MIN(-2147483647 -1), INT_MAX2147483647, &i);
70 if (!ret)
71 *(int *)value = i;
72 break;
73 case CFG_BYTE:
74 ret = shr_parse_u64(arg, UINT8_MAX(255), &u);
75 if (!ret)
76 *(uint8_t *)value = u;
77 break;
78 case CFG_SHORT:
79 ret = shr_parse_u64(arg, UINT16_MAX(65535), &u);
80 if (!ret)
81 *(uint16_t *)value = u;
82 break;
83 case CFG_POSITIVE:
84 ret = shr_parse_u64(arg, UINT32_MAX(4294967295U), &u);
85 if (!ret)
86 *(uint32_t *)value = u;
87 break;
88 case CFG_INCREMENT:
89 *(int *)value += 1;
90 break;
91 case CFG_ENUM:
92 case CFG_ENUM_LIST:
93 ret = -EINVAL22;
94 break;
95 case CFG_LONG:
96 ret = shr_parse_u64(arg, UINT64_MAX(18446744073709551615UL), &u);
97 if (!ret)
98 *(uint64_t *)value = u;
99 break;
100 case CFG_LONG_SUFFIX:
101 if (!isdigit((unsigned char)*arg)((*__ctype_b_loc ())[(int) (((unsigned char)*arg))] & (unsigned
short int) _ISdigit)
)
26
Dereference of null pointer (loaded from variable 'arg')
102 return -EINVAL22;
103 ret = shr_suffix_binary_parse(arg, &endptr, &u);
104 if (!ret)
105 *(uint64_t *)value = u;
106 break;
107 case CFG_DOUBLE:
108 ret = shr_parse_double(arg, &d);
109 if (!ret)
110 *(double *)value = d;
111 break;
112 case CFG_FLAG:
113 *(bool_Bool *)value = true1;
114 break;
115 case CFG_GROUP_SEPARATOR:
116 case CFG_INCLUDE:
117 break;
118 }
119
120 return ret ? -EINVAL22 : 0;
121}
122
123static void argconfig_set_opt_val(enum argconfig_types type,
124 union argconfig_val *opt_val, void *val)
125{
126 switch (type) {
127 case CFG_FLAG:
128 *(bool_Bool *)val = opt_val->bool_val;
129 break;
130 case CFG_LONG_SUFFIX:
131 *(uint64_t *)val = opt_val->long_suffix;
132 break;
133 case CFG_POSITIVE:
134 *(uint32_t *)val = opt_val->positive;
135 break;
136 case CFG_INT:
137 case CFG_ENUM:
138 *(int *)val = opt_val->int_val;
139 break;
140 case CFG_LONG:
141 *(uint64_t *)val = opt_val->long_val;
142 break;
143 case CFG_DOUBLE:
144 *(double *)val = opt_val->double_val;
145 break;
146 case CFG_BYTE:
147 *(uint8_t *)val = opt_val->byte;
148 break;
149 case CFG_SHORT:
150 *(uint16_t *)val = opt_val->short_val;
151 break;
152 case CFG_INCREMENT:
153 *(int *)val = opt_val->increment;
154 break;
155 case CFG_STRING:
156 case CFG_FILE:
157 case CFG_DIR:
158 *(char **)val = opt_val->string;
159 break;
160 case CFG_ENUM_LIST:
161 case CFG_GROUP_SEPARATOR:
162 case CFG_INCLUDE:
163 break;
164 }
165}
166
167static struct argconfig_opt_val *
168argconfig_match_val(struct argconfig_opt_val *v, const char *str)
169{
170 size_t len = strlen(str);
171 struct argconfig_opt_val *match = NULL((void*)0);
172
173 for (; v->str; v++) {
174 if (strncasecmp(str, v->str, len))
175 continue;
176
177 if (len == strlen(v->str))
178 return v;
179
180 if (match)
181 return NULL((void*)0); /* multiple matches; input is ambiguous */
182
183 match = v;
184 }
185
186 return match;
187}
188
189static int argconfig_parse_val(struct argconfig_commandline_options *s,
190 char *arg)
191{
192 struct argconfig_opt_val *v = s->opt_val;
193
194 if (v)
18
Assuming 'v' is null
19
Taking false branch
195 v = argconfig_match_val(v, arg);
196 if (!v
19.1
'v' is null
)
20
Taking true branch
197 return argconfig_parse_type(s, arg);
21
Passing value via 2nd parameter 'arg'
22
Calling 'argconfig_parse_type'
198
199 argconfig_set_opt_val(v->type, &v->val, s->default_value);
200 return 0;
201}
202
203int ref_argconfig_parse(int argc, char *argv[],
204 struct argconfig_commandline_options *options,
205 char ***operands, int *nr_operands)
206{
207 __cleanup_free__attribute__((cleanup(shr_freep))) char *short_opts = NULL((void*)0);
208 __cleanup_free__attribute__((cleanup(shr_freep))) struct option *long_opts = NULL((void*)0);
209 __cleanup_free__attribute__((cleanup(shr_freep))) int *long_opt_map = NULL((void*)0);
210 struct argconfig_commandline_options *s;
211 int c, long_opt_index = 0, opt_index = 0, short_index = 0;
212 int options_count = 0;
213 int ret = 0;
214
215 errno(*__errno_location ()) = 0;
216 for (s = options; s->option; s++)
217 options_count++;
218
219 long_opts = calloc(options_count + 2, sizeof(struct option));
220 short_opts = calloc(options_count * 3 + 3, sizeof(*short_opts));
221 long_opt_map = calloc(options_count + 2, sizeof(*long_opt_map));
222
223 if (!long_opts || !short_opts || !long_opt_map) {
224 fprintf(stderrstderr, "failed to allocate memory for opts: %s\n",
225 libnvme_strerror(errno(*__errno_location ())));
226 return -errno(*__errno_location ());
227 }
228
229 for (s = options, opt_index = 0; s->option; s++, opt_index++) {
230 s->seen = false0;
231 if (s->config_type == CFG_GROUP_SEPARATOR)
232 continue;
233 if (s->short_option) {
234 short_opts[short_index++] = s->short_option;
235 if (s->argument_type == ARGCONFIG_ARG_REQUIRED ||
236 s->argument_type == ARGCONFIG_ARG_OPTIONAL)
237 short_opts[short_index++] = ':';
238 if (s->argument_type == ARGCONFIG_ARG_OPTIONAL)
239 short_opts[short_index++] = ':';
240 }
241 if (!is_null_or_empty(s->option)) {
242 long_opts[long_opt_index].name = s->option;
243 long_opts[long_opt_index].has_arg = has_arg(s);
244 long_opt_map[long_opt_index] = opt_index;
245 long_opt_index++;
246 }
247 }
248
249 long_opts[long_opt_index].name = "help";
250 long_opts[long_opt_index].val = 'h';
251
252 short_opts[short_index++] = '?';
253 short_opts[short_index] = 'h';
254
255 optind = 0;
256 while ((c = getopt_long_only(argc, argv, short_opts, long_opts,
257 &long_opt_index)) != -1) {
258 if (c) {
259 if (c == '?' || c == 'h') {
260 ret = -EINVAL22;
261 break;
262 }
263 for (opt_index = 0; opt_index < options_count;
264 opt_index++) {
265 if (c == options[opt_index].short_option)
266 break;
267 }
268 if (opt_index == options_count)
269 continue;
270 } else {
271 opt_index = long_opt_map[long_opt_index];
272 }
273
274 s = &options[opt_index];
275 s->seen = true1;
276
277 if (!s->default_value)
278 continue;
279
280 ret = argconfig_parse_val(s, optarg);
281 if (ret)
282 break;
283 }
284
285 *operands = &argv[optind];
286 *nr_operands = argc - optind;
287
288 return ret;
289}
290
291/* Global options: stop at the first non-option argument. */
292int ref_argconfig_parse_global(int argc, char *argv[],
293 struct argconfig_commandline_options *options,
294 int *argind)
295{
296 __cleanup_free__attribute__((cleanup(shr_freep))) char *short_opts = NULL((void*)0);
297 __cleanup_free__attribute__((cleanup(shr_freep))) struct option *long_opts = NULL((void*)0);
298 __cleanup_free__attribute__((cleanup(shr_freep))) int *long_opt_map = NULL((void*)0);
299 struct argconfig_commandline_options *s;
300 int c, long_opt_index = 0, opt_index = 0;
301 int short_index = 0, options_count = 0;
302 int ret = 0;
303
304 errno(*__errno_location ()) = 0;
305 for (s = options; s->option; s++)
1
Loop condition is false. Execution continues on line 308
306 options_count++;
307
308 long_opts = calloc(options_count + 2, sizeof(struct option));
309 /* '+' prefix: stop at the first non-option argument (the subcommand) */
310 short_opts = calloc(options_count * 3 + 4, sizeof(*short_opts));
311 long_opt_map = calloc(options_count + 2, sizeof(*long_opt_map));
312
313 if (!long_opts || !short_opts || !long_opt_map) {
2
Assuming 'long_opts' is non-null
3
Assuming 'short_opts' is non-null
4
Assuming 'long_opt_map' is non-null
5
Taking false branch
314 fprintf(stderrstderr, "failed to allocate memory for opts: %s\n",
315 libnvme_strerror(errno(*__errno_location ())));
316 return -errno(*__errno_location ());
317 }
318
319 short_opts[short_index++] = '+';
320
321 for (s = options, opt_index = 0; s->option; s++, opt_index++) {
6
Loop condition is false. Execution continues on line 341
322 s->seen = false0;
323 if (s->config_type == CFG_GROUP_SEPARATOR)
324 continue;
325 if (s->short_option) {
326 short_opts[short_index++] = s->short_option;
327 if (s->argument_type == ARGCONFIG_ARG_REQUIRED ||
328 s->argument_type == ARGCONFIG_ARG_OPTIONAL)
329 short_opts[short_index++] = ':';
330 if (s->argument_type == ARGCONFIG_ARG_OPTIONAL)
331 short_opts[short_index++] = ':';
332 }
333 if (!is_null_or_empty(s->option)) {
334 long_opts[long_opt_index].name = s->option;
335 long_opts[long_opt_index].has_arg = has_arg(s);
336 long_opt_map[long_opt_index] = opt_index;
337 long_opt_index++;
338 }
339 }
340
341 optind = 0;
342 while ((c = getopt_long(argc, argv, short_opts,
7
Value assigned to 'optarg'
8
Assuming the condition is true
343 long_opts, &long_opt_index)) != -1) {
344 if (c == '?' || c == ':') {
9
Assuming the condition is false
10
Assuming the condition is false
11
Taking false branch
345 ret = -EINVAL22;
346 break;
347 }
348 if (c) {
12
Assuming 'c' is 0
13
Taking false branch
349 for (opt_index = 0; opt_index < options_count;
350 opt_index++) {
351 if (c == options[opt_index].short_option)
352 break;
353 }
354 if (opt_index == options_count) {
355 ret = -EINVAL22;
356 break;
357 }
358 } else {
359 opt_index = long_opt_map[long_opt_index];
360 }
361
362 s = &options[opt_index];
363 s->seen = true1;
364
365 if (!s->default_value)
14
Assuming field 'default_value' is non-null
15
Taking false branch
366 continue;
367
368 ret = argconfig_parse_val(s, optarg);
16
Passing value via 2nd parameter 'arg'
17
Calling 'argconfig_parse_val'
369 if (ret)
370 break;
371 }
372
373 *argind = optind;
374
375 return ret;
376}