Bug Summary

File:.build-ci/../plugin.c
Warning:line 285, column 13
Potential leak of memory pointed to by 'sub_argv'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name plugin.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 pic -pic-level 2 -pic-is-pie -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/lib/llvm-19/lib/clang/19 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -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/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -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 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-06-24-175442-590-1 -x c ../plugin.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2#include <errno(*__errno_location ()).h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6
7#include <libnvme.h>
8
9#include "nvme.h"
10#include "plugin.h"
11#include "util/argconfig.h"
12#include "util/cleanup.h"
13
14static int version_cmd(struct plugin *plugin)
15{
16 struct program *prog = plugin->parent;
17
18 if (plugin->name) {
19 printf("%s %s version %s (git %s)\n",
20 prog->name, plugin->name, plugin->version, GIT_VERSION"3.0-a.5-312-g291cba4");
21 } else {
22 printf("%s version %s (git %s)\n",
23 prog->name, prog->version, GIT_VERSION"3.0-a.5-312-g291cba4");
24 }
25 printf("libnvme version %s (git %s)\n",
26 libnvme_get_version(LIBNVME_VERSION_PROJECT),
27 libnvme_get_version(LIBNVME_VERSION_GIT));
28 return 0;
29}
30
31static int help(int argc, char **argv, struct plugin *plugin)
32{
33 char man[0x100];
34 struct program *prog = plugin->parent;
35 char *str = argv[1];
36 int i;
37
38 if (argc == 1) {
39 general_help(plugin, NULL((void*)0));
40 return 0;
41 }
42
43 for (i = 0; plugin->commands[i]; i++) {
44 struct command *command = plugin->commands[i];
45
46 if (strcmp(str, command->name))
47 if (!command->alias ||
48 (command->alias && strcmp(str, command->alias)))
49 continue;
50
51 if (plugin->name)
52 snprintf(man, sizeof(man), "%s-%s-%s", prog->name,
53 plugin->name, command->name);
54 else
55 snprintf(man, sizeof(man), "%s-%s", prog->name, command->name);
56 if (execlp("man", "man", man, (char *)NULL((void*)0)))
57 perror(argv[1]);
58 }
59
60 general_help(plugin, str);
61
62 return 0;
63}
64
65static void usage_cmd(struct plugin *plugin)
66{
67 struct program *prog = plugin->parent;
68
69 if (plugin->name)
70 printf("usage: %s %s %s\n", prog->name, plugin->name, prog->usage);
71 else
72 printf("usage: %s %s\n", prog->name, prog->usage);
73}
74
75void general_help(struct plugin *plugin, char *str)
76{
77 struct program *prog = plugin->parent;
78 struct plugin *extension;
79 unsigned int i = 0;
80 unsigned int padding = 15;
81 unsigned int curr_length = 0;
82
83 printf("%s-%s\n", prog->name, prog->version);
84
85 usage_cmd(plugin);
86
87 printf("\n");
88 print_word_wrapped(prog->desc, 0, 0, stdoutstdout);
89 printf("\n");
90
91 if (plugin->desc) {
92 printf("\n");
93 print_word_wrapped(plugin->desc, 0, 0, stdoutstdout);
94 printf("\n");
95 }
96
97 printf("\nThe following are all implemented sub-commands:\n");
98 if (str)
99 printf("Note: Only sub-commands including %s\n", str);
100
101 /*
102 * iterate through all commands to get maximum length
103 * Still need to handle the case of ultra long strings, help messages, etc
104 */
105 for (; plugin->commands[i]; i++) {
106 curr_length = 2 + strlen(plugin->commands[i]->name);
107 if (padding < curr_length)
108 padding = curr_length;
109 }
110
111 i = 0;
112 for (; plugin->commands[i]; i++) {
113 if (!str || strstr(plugin->commands[i]->name, str))
114 printf(" %-*s %s\n", padding, plugin->commands[i]->name,
115 plugin->commands[i]->help);
116 }
117
118 if (!str || strstr("version", str))
119 printf(" %-*s %s\n", padding, "version", "Shows the program version");
120 if (!str || strstr("help", str))
121 printf(" %-*s %s\n", padding, "help", "Display this help");
122 printf("\n");
123
124 if (plugin->name)
125 printf("See '%s %s help <command>' for more information on a specific command\n",
126 prog->name, plugin->name);
127 else
128 printf("See '%s help <command>' for more information on a specific command\n",
129 prog->name);
130
131 /*
132 * The first plugin is the built-in. If we're not showing help for the
133 * built-in, don't show the program's other extensions
134 */
135 if (plugin->name)
136 return;
137
138 extension = prog->extensions->next;
139 if (!extension)
140 return;
141
142 printf("\nThe following are all installed plugin extensions:\n");
143 if (str)
144 printf("Note: Only extensions including %s\n", str);
145
146 while (extension) {
147 if (!str || strstr(extension->name, str))
148 printf(" %-*s %s\n", 15, extension->name, extension->desc);
149 extension = extension->next;
150 }
151 printf("\nSee '%s <plugin> help' for more information on a plugin\n",
152 prog->name);
153}
154
155int handle_plugin(int argc, char **argv, struct plugin *plugin)
156{
157 char use[0x100];
158 struct plugin *extension;
159 struct program *prog = plugin->parent;
160 struct command **cmd = plugin->commands;
161 struct command *cr = NULL((void*)0);
162 bool_Bool opt_help = false0, opt_version = false0;
163 bool_Bool cr_valid = false0;
164 char *str;
165 int err;
166
167 if (!argc) {
1
Assuming 'argc' is not equal to 0
2
Taking false branch
168 general_help(plugin, NULL((void*)0));
169 return 0;
170 }
171
172 /*
173 * look for global options before the sub command parser and
174 * pre-fill the global nvme_args variable.
175 */
176 NVME_ARGS(global_opts,struct argconfig_commandline_options global_opts[] = { {"", 0
, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0
, ((void*)0)}, {"help", 'h', ((void*)0), CFG_FLAG, &opt_help
, 0, "show help text", 0, }, {"version", 'V', ((void*)0), CFG_FLAG
, &opt_version, 0, "show version", 0, }, {"", 0, ((void*)
0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Global options", 0, (
(void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT, &nvme_args
.verbose, 0, "Increase output verbosity", 0, }, {"output-format"
, 'o', "FMT", CFG_STRING, &nvme_args.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
177 OPT_FLAG("help", 'h', &opt_help, "show help text"),struct argconfig_commandline_options global_opts[] = { {"", 0
, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0
, ((void*)0)}, {"help", 'h', ((void*)0), CFG_FLAG, &opt_help
, 0, "show help text", 0, }, {"version", 'V', ((void*)0), CFG_FLAG
, &opt_version, 0, "show version", 0, }, {"", 0, ((void*)
0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Global options", 0, (
(void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT, &nvme_args
.verbose, 0, "Increase output verbosity", 0, }, {"output-format"
, 'o', "FMT", CFG_STRING, &nvme_args.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
178 OPT_FLAG("version", 'V', &opt_version, "show version"))struct argconfig_commandline_options global_opts[] = { {"", 0
, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0
, ((void*)0)}, {"help", 'h', ((void*)0), CFG_FLAG, &opt_help
, 0, "show help text", 0, }, {"version", 'V', ((void*)0), CFG_FLAG
, &opt_version, 0, "show version", 0, }, {"", 0, ((void*)
0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Global options", 0, (
(void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT, &nvme_args
.verbose, 0, "Increase output verbosity", 0, }, {"output-format"
, 'o', "FMT", CFG_STRING, &nvme_args.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
;
179 err = argconfig_parse_global(argc, argv, global_opts);
180 if (err) {
3
Assuming 'err' is 0
4
Taking false branch
181 general_help(plugin, NULL((void*)0));
182 return err;
183 }
184
185 argc -= optind;
186 argv += optind;
187
188 if (opt_help) {
5
Assuming 'opt_help' is false
6
Taking false branch
189 __cleanup_free__attribute__((cleanup(freep))) char **help_argv = NULL((void*)0);
190 __cleanup_free__attribute__((cleanup(freep))) char *help_name = NULL((void*)0);
191
192 if (argc <= 0) {
193 general_help(plugin, NULL((void*)0));
194 return 0;
195 }
196
197 help_argv = malloc((argc + 1) * sizeof(*help_argv));
198 if (!help_argv)
199 return -ENOMEM12;
200
201 help_name = strdup("help");
202 if (!help_name)
203 return -ENOMEM12;
204
205 help_argv[0] = help_name;
206 memcpy(&help_argv[1], argv, argc * sizeof(*argv));
207 return help(argc + 1, help_argv, plugin);
208 }
209
210 if (opt_version)
7
Assuming 'opt_version' is false
8
Taking false branch
211 return version_cmd(plugin);
212
213 if (!argc) {
9
Assuming 'argc' is not equal to 0
10
Taking false branch
214 general_help(plugin, NULL((void*)0));
215 return 0;
216 }
217
218 str = argv[0];
219
220 if (!plugin->name)
11
Assuming field 'name' is null
12
Taking true branch
221 snprintf(use, sizeof(use), "%s %s <device> [OPTIONS]", prog->name, str);
222 else
223 snprintf(use, sizeof(use), "%s %s %s <device> [OPTIONS]", prog->name, plugin->name, str);
224 argconfig_append_usage(use);
225
226 if (!strcmp(str, "help"))
13
Assuming the condition is false
14
Taking false branch
227 return help(argc, argv, plugin);
228 if (!strcmp(str, "version"))
15
Assuming the condition is false
16
Taking false branch
229 return version_cmd(plugin);
230
231 while (*cmd) {
232 if (!strcmp(str, (*cmd)->name) ||
233 ((*cmd)->alias && !strcmp(str, (*cmd)->alias)))
234 return (*cmd)->fn(argc, argv, *cmd, plugin);
235 if (!strncmp(str, (*cmd)->name, strlen(str))) {
236 if (cr) {
237 cr_valid = false0;
238 } else {
239 cr = *cmd;
240 cr_valid = true1;
241 }
242 }
243 cmd++;
244 }
245
246 if (cr
16.1
'cr' is null
&& cr_valid) {
247 snprintf(use, sizeof(use), "%s %s <device> [OPTIONS]", prog->name, cr->name);
248 argconfig_append_usage(use);
249 return cr->fn(argc, argv, cr, plugin);
250 }
251
252 /* Check extensions only if this is running the built-in plugin */
253 if (plugin->name
16.2
Field 'name' is null
) {
17
Taking false branch
254 printf("ERROR: Invalid sub-command '%s' for plugin %s\n", str, plugin->name);
255 return -ENOTTY25;
256 }
257
258 extension = plugin->next;
259 while (extension) {
18
Loop condition is true. Entering loop body
21
Loop condition is false. Execution continues on line 269
260 if (!strcmp(str, extension->name))
19
Assuming the condition is false
20
Taking false branch
261 return handle_plugin(argc, argv, extension);
262 extension = extension->next;
263 }
264
265 /*
266 * If the command is executed with the extension name and
267 * command together ("plugin-command"), run the plug in
268 */
269 extension = plugin->next;
270 while (extension) {
22
Loop condition is true. Entering loop body
271 if (!strncmp(str, extension->name, strlen(extension->name))) {
23
Assuming the condition is true
24
Taking true branch
272 __cleanup_free__attribute__((cleanup(freep))) char **sub_argv = NULL((void*)0);
273 __cleanup_free__attribute__((cleanup(freep))) char *name_copy = NULL((void*)0);
274
275 sub_argv = malloc((argc + 1) * sizeof(*sub_argv));
25
Memory is allocated
276 if (!sub_argv)
26
Assuming 'sub_argv' is non-null
27
Taking false branch
277 return -ENOMEM12;
278
279 argv[0] += strlen(extension->name);
280 while (*argv[0] == '-')
28
Assuming the condition is false
29
Loop condition is false. Execution continues on line 283
281 argv[0]++;
282
283 name_copy = strdup(extension->name);
284 if (!name_copy)
30
Assuming 'name_copy' is null
31
Taking true branch
285 return -ENOMEM12;
32
Potential leak of memory pointed to by 'sub_argv'
286
287 sub_argv[0] = name_copy;
288 memcpy(&sub_argv[1], argv, argc * sizeof(*argv));
289
290 return handle_plugin(argc + 1, sub_argv, extension);
291 }
292 extension = extension->next;
293 }
294 printf("ERROR: Invalid sub-command '%s'\n", str);
295 return -ENOTTY25;
296}