Bug Summary

File:.build-ci/../src/argconfig.c
Warning:line 408, column 3
Potential leak of memory pointed to by 'short_opts'

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.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 /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=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-09-23-073103-589-1 -x c ../src/argconfig.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright 2014 PMC-Sierra, Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 */
20
21/*
22 *
23 * Author: Logan Gunthorpe
24 *
25 * Date: Oct 23 2014
26 *
27 * Description:
28 * Functions for parsing command line options.
29 *
30 */
31
32#include <errno(*__errno_location ()).h>
33#include <getopt.h>
34#include <locale.h>
35#include <stdarg.h>
36#include <stdbool.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40
41#include <libnvme.h>
42
43#include <shared/suffix-util.h>
44#include <shared/wrap-util.h>
45
46#include "argconfig.h"
47#include "cleanup.h"
48
49static bool_Bool is_null_or_empty(const char *s)
50{
51 return !s || !*s;
52}
53
54static const char *append_usage_str = "";
55
56void argconfig_append_usage(const char *str)
57{
58 append_usage_str = str;
59}
60
61static void show_option(const struct argconfig_commandline_options *option)
62{
63 char buffer[0x1000];
64 char *b = buffer;
65
66 b += sprintf(b, " [ ");
67 if (option->option) {
68 b += sprintf(b, " --%s", option->option);
69 if (option->argument_type == optional_argument2)
70 b += sprintf(b, "[=<%s>]", option->meta ? option->meta : "arg");
71 if (option->argument_type == required_argument1)
72 b += sprintf(b, "=<%s>", option->meta ? option->meta : "arg");
73 if (option->short_option)
74 b += sprintf(b, ",");
75 }
76 if (option->short_option) {
77 b += sprintf(b, " -%c", option->short_option);
78 if (option->argument_type == optional_argument2)
79 b += sprintf(b, " [<%s>]", option->meta ? option->meta : "arg");
80 if (option->argument_type == required_argument1)
81 b += sprintf(b, " <%s>", option->meta ? option->meta : "arg");
82 }
83 b += sprintf(b, " ] ");
84
85 fprintf(stderrstderr, "%s", buffer);
86 if (option->help) {
87 shr_print_word_wrapped("--- ", 40, b - buffer, stderrstderr);
88 shr_print_word_wrapped(option->help, 44, 44, stderrstderr);
89 }
90 fprintf(stderrstderr, "\n");
91}
92
93void argconfig_print_help(const char *program_desc,
94 struct argconfig_commandline_options *s)
95{
96 const char *pending_header = "Options";
97 bool_Bool header_printed = false0;
98
99 fprintf(stderrstderr, "\033[1mUsage: %s\033[0m\n\n",
100 append_usage_str);
101
102 shr_print_word_wrapped(program_desc, 0, 0, stderrstderr);
103 fprintf(stderrstderr, "\n");
104
105 if (!s || !s->option)
106 return;
107
108 for (; s->option; s++) {
109 if (s->config_type == CFG_GROUP_SEPARATOR) {
110 pending_header = s->help;
111 header_printed = false0;
112 continue;
113 }
114 if (!header_printed) {
115 fprintf(stderrstderr, "\n\033[1m%s:\033[0m\n", pending_header);
116 header_printed = true1;
117 }
118 if (!s->hidden)
119 show_option(s);
120 }
121}
122
123static int argconfig_error(char *type, const char *opt, const char *arg)
124{
125 fprintf(stderrstderr, "Expected %s argument for '%s' but got '%s'!\n", type, opt, arg);
126 return -EINVAL22;
127}
128
129static int argconfig_parse_type(struct argconfig_commandline_options *s)
130{
131 void *value = s->default_value;
132 char *endptr;
133 int ret = 0;
134
135 errno(*__errno_location ()) = 0; /* To distinguish success/failure after strtol/stroul call */
136
137 switch (s->config_type) {
138 case CFG_STRING:
139 *(char **)value = optarg;
140 break;
141 case CFG_INT:
142 *(int *)value = strtol(optarg, &endptr, 0);
143 if (errno(*__errno_location ()) || optarg == endptr)
144 ret = argconfig_error("integer", s->option, optarg);
145 break;
146 case CFG_BYTE: {
147 unsigned long tmp = strtoul(optarg, &endptr, 0);
148
149 if (errno(*__errno_location ()) || tmp >= 1 << 8 || optarg == endptr)
150 ret = argconfig_error("byte", s->option, optarg);
151 else
152 *(uint8_t *)value = tmp;
153 break;
154 }
155 case CFG_SHORT: {
156 unsigned long tmp = strtoul(optarg, &endptr, 0);
157
158 if (errno(*__errno_location ()) || tmp >= 1 << 16 || optarg == endptr)
159 ret = argconfig_error("short", s->option, optarg);
160 else
161 *(uint16_t *)value = tmp;
162 break;
163 }
164 case CFG_POSITIVE: {
165 uint32_t tmp = strtoul(optarg, &endptr, 0);
166
167 if (errno(*__errno_location ()) || optarg == endptr)
168 ret = argconfig_error("word", s->option, optarg);
169 else
170 *(uint32_t *)value = tmp;
171 break;
172 }
173 case CFG_INCREMENT:
174 *(int *)value += 1;
175 break;
176 case CFG_LONG:
177 *(unsigned long *)value = strtoul(optarg, &endptr, 0);
178 if (errno(*__errno_location ()) || optarg == endptr)
179 ret = argconfig_error("long integer", s->option, optarg);
180 break;
181 case CFG_LONG_SUFFIX:
182 ret = shr_suffix_binary_parse(optarg, &endptr, (uint64_t *)value);
183 if (ret)
184 argconfig_error("long suffixed integer", s->option, optarg);
185 break;
186 case CFG_DOUBLE:
187 *(double *)value = strtod(optarg, &endptr);
188 if (errno(*__errno_location ()) || optarg == endptr)
189 ret = argconfig_error("float", s->option, optarg);
190 break;
191 case CFG_FLAG:
192 *(bool_Bool *)value = true1;
193 break;
194 case CFG_GROUP_SEPARATOR:
195 break;
196 }
197
198 return ret;
199}
200
201static void argconfig_set_opt_val(enum argconfig_types type, union argconfig_val *opt_val, void *val)
202{
203 switch (type) {
204 case CFG_FLAG:
205 *(bool_Bool *)val = opt_val->bool_val;
206 break;
207 case CFG_LONG_SUFFIX:
208 *(uint64_t *)val = opt_val->long_suffix;
209 break;
210 case CFG_POSITIVE:
211 *(uint32_t *)val = opt_val->positive;
212 break;
213 case CFG_INT:
214 *(int *)val = opt_val->int_val;
215 break;
216 case CFG_LONG:
217 *(unsigned long *)val = opt_val->long_val;
218 break;
219 case CFG_DOUBLE:
220 *(double *)val = opt_val->double_val;
221 break;
222 case CFG_BYTE:
223 *(uint8_t *)val = opt_val->byte;
224 break;
225 case CFG_SHORT:
226 *(uint16_t *)val = opt_val->short_val;
227 break;
228 case CFG_INCREMENT:
229 *(int *)val = opt_val->increment;
230 break;
231 case CFG_STRING:
232 *(char **)val = opt_val->string;
233 break;
234 case CFG_GROUP_SEPARATOR:
235 break;
236 }
237}
238
239static struct argconfig_opt_val *
240argconfig_match_val(struct argconfig_opt_val *v, const char *str)
241{
242 size_t len = strlen(str);
243 struct argconfig_opt_val *match = NULL((void*)0);
244
245 for (; v->str; v++) {
246 if (strncasecmp(str, v->str, len))
247 continue;
248
249 if (len == strlen(v->str))
250 return v;
251
252 if (match)
253 return NULL((void*)0); /* multiple matches; input is ambiguous */
254
255 match = v;
256 }
257
258 return match;
259}
260
261static int argconfig_parse_val(struct argconfig_commandline_options *s)
262{
263 struct argconfig_opt_val *v = s->opt_val;
264
265 if (v)
266 v = argconfig_match_val(v, optarg);
267 if (!v)
268 return argconfig_parse_type(s);
269
270 argconfig_set_opt_val(v->type, &v->val, s->default_value);
271 return 0;
272}
273
274static bool_Bool argconfig_check_verbose(struct argconfig_commandline_options *s)
275{
276 for (; s && s->option; s++) {
277 if (!strcmp(s->option, "verbose") &&
278 s->config_type == CFG_INCREMENT)
279 return s->seen;
280 }
281
282 return false0;
283}
284
285static argconfig_parse_hook_fn argconfig_parse_hook;
286
287void argconfig_set_parse_hook(argconfig_parse_hook_fn hook)
288{
289 argconfig_parse_hook = hook;
290}
291
292int argconfig_parse(int argc, char *argv[], const char *program_desc,
293 struct argconfig_commandline_options *options)
294{
295 __cleanup_free__attribute__((cleanup(shr_freep))) char *short_opts = NULL((void*)0);
296 __cleanup_free__attribute__((cleanup(shr_freep))) struct option *long_opts = NULL((void*)0);
297 __cleanup_free__attribute__((cleanup(shr_freep))) int *long_opt_map = NULL((void*)0);
298 struct argconfig_commandline_options *s;
299 int c, long_opt_index = 0, opt_index = 0, short_index = 0, options_count = 0;
300 int ret = 0;
301
302 if (argconfig_parse_hook)
303 return argconfig_parse_hook(argc, argv, program_desc, options);
304
305 errno(*__errno_location ()) = 0;
306 for (s = options; s->option; s++)
307 options_count++;
308
309 long_opts = calloc(options_count + 2, sizeof(struct option));
310 short_opts = calloc(options_count * 3 + 3, 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) {
314 fprintf(stderrstderr, "failed to allocate memory for opts: %s\n", libnvme_strerror(errno(*__errno_location ())));
315 return -errno(*__errno_location ());
316 }
317
318 for (s = options, opt_index = 0; s->option; s++, opt_index++) {
319 s->seen = false0;
320 if (s->config_type == CFG_GROUP_SEPARATOR)
321 continue;
322 if (s->short_option) {
323 short_opts[short_index++] = s->short_option;
324 if (s->argument_type == required_argument1 ||
325 s->argument_type == optional_argument2)
326 short_opts[short_index++] = ':';
327 if (s->argument_type == optional_argument2)
328 short_opts[short_index++] = ':';
329 }
330 if (!is_null_or_empty(s->option)) {
331 long_opts[long_opt_index].name = s->option;
332 long_opts[long_opt_index].has_arg = s->argument_type;
333 long_opt_map[long_opt_index] = opt_index;
334 long_opt_index++;
335 }
336 }
337
338 long_opts[long_opt_index].name = "help";
339 long_opts[long_opt_index].val = 'h';
340
341 short_opts[short_index++] = '?';
342 short_opts[short_index] = 'h';
343
344 optind = 0;
345 while ((c = getopt_long_only(argc, argv, short_opts, long_opts, &long_opt_index)) != -1) {
346 if (c) {
347 if (c == '?' || c == 'h') {
348 argconfig_print_help(program_desc, options);
349 ret = -EINVAL22;
350 break;
351 }
352 for (opt_index = 0; opt_index < options_count; opt_index++) {
353 if (c == options[opt_index].short_option)
354 break;
355 }
356 if (opt_index == options_count)
357 continue;
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)
366 continue;
367
368 ret = argconfig_parse_val(s);
369 if (ret)
370 break;
371 }
372
373 if (!argconfig_check_verbose(options))
374 setlocale(LC_ALL6, "C");
375
376 return ret;
377}
378
379/*
380 * Parse global (pre-subcommand) options from argc/argv. Stops at the first
381 * non-option argument (i.e. the subcommand name) so that subcommand-specific
382 * options are left in place for the subcommand dispatcher. After a successful
383 * call, optind points to the first non-option argument (the subcommand).
384 *
385 * Returns 0 on success, negative errno on failure.
386 */
387int argconfig_parse_global(int argc, char *argv[],
388 struct argconfig_commandline_options *options)
389{
390 __cleanup_free__attribute__((cleanup(shr_freep))) char *short_opts = NULL((void*)0);
391 __cleanup_free__attribute__((cleanup(shr_freep))) struct option *long_opts = NULL((void*)0);
392 __cleanup_free__attribute__((cleanup(shr_freep))) int *long_opt_map = NULL((void*)0);
393 struct argconfig_commandline_options *s;
394 int c, long_opt_index = 0, opt_index = 0;
395 int short_index = 0, options_count = 0;
396 int ret = 0;
397
398 errno(*__errno_location ()) = 0;
399 for (s = options; s->option; s++)
1
Loop condition is false. Execution continues on line 402
400 options_count++;
401
402 long_opts = calloc(options_count + 2, sizeof(struct option));
403 /* '+' prefix: stop at the first non-option argument (the subcommand) */
404 short_opts = calloc(options_count * 3 + 4, sizeof(*short_opts));
2
Memory is allocated
405 long_opt_map = calloc(options_count + 2, sizeof(*long_opt_map));
406
407 if (!long_opts || !short_opts || !long_opt_map) {
3
Assuming 'long_opts' is null
408 fprintf(stderrstderr, "failed to allocate memory for opts: %s\n",
4
Potential leak of memory pointed to by 'short_opts'
409 libnvme_strerror(errno(*__errno_location ())));
410 return -errno(*__errno_location ());
411 }
412
413 short_opts[short_index++] = '+';
414
415 for (s = options, opt_index = 0; s->option; s++, opt_index++) {
416 s->seen = false0;
417 if (s->config_type == CFG_GROUP_SEPARATOR)
418 continue;
419 if (s->short_option) {
420 short_opts[short_index++] = s->short_option;
421 if (s->argument_type == required_argument1 ||
422 s->argument_type == optional_argument2)
423 short_opts[short_index++] = ':';
424 if (s->argument_type == optional_argument2)
425 short_opts[short_index++] = ':';
426 }
427 if (!is_null_or_empty(s->option)) {
428 long_opts[long_opt_index].name = s->option;
429 long_opts[long_opt_index].has_arg = s->argument_type;
430 long_opt_map[long_opt_index] = opt_index;
431 long_opt_index++;
432 }
433 }
434
435 optind = 0;
436 while ((c = getopt_long(argc, argv, short_opts,
437 long_opts, &long_opt_index)) != -1) {
438 if (c == '?' || c == ':') {
439 ret = -EINVAL22;
440 break;
441 }
442 if (c) {
443 for (opt_index = 0; opt_index < options_count;
444 opt_index++) {
445 if (c == options[opt_index].short_option)
446 break;
447 }
448 if (opt_index == options_count) {
449 ret = -EINVAL22;
450 break;
451 }
452 } else {
453 opt_index = long_opt_map[long_opt_index];
454 }
455
456 s = &options[opt_index];
457 s->seen = true1;
458
459 if (!s->default_value)
460 continue;
461
462 ret = argconfig_parse_val(s);
463 if (ret)
464 break;
465 }
466
467 return ret;
468}
469
470bool_Bool argconfig_parse_seen(struct argconfig_commandline_options *s,
471 const char *option)
472{
473 for (; s && s->option; s++) {
474 if (!strcmp(s->option, option))
475 return s->seen;
476 }
477
478 return false0;
479}