Bug Summary

File:.build-ci/../src/global-ctx.c
Warning:line 200, column 10
Potential leak of memory pointed to by 'opt'

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 global-ctx.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/global-ctx.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * This file is part of nvme-cli.
4 * Copyright (c) 2026 SUSE Software Solutions
5 *
6 * Authors: Daniel Wagner <dwagner@suse.de>
7 */
8#include <errno(*__errno_location ()).h>
9#include <fcntl.h>
10#include <getopt.h>
11#include <stdbool.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include <libnvme-mi.h>
17#include <libnvme.h>
18
19#include <shared/string-util.h>
20
21#include "argconfig.h"
22#include "args.h"
23#include "cleanup.h"
24#include "global-ctx.h"
25#include "logging.h"
26#include "nvme-print.h"
27
28static int check_arg_dev(int argc, char **argv)
29{
30 if (optind >= argc) {
31 errno(*__errno_location ()) = EINVAL22;
32 nvme_show_perror(argv[0]);
33 return -EINVAL22;
34 }
35 return 0;
36}
37
38static int get_transport_handle(struct libnvme_global_ctx *ctx, int argc,
39 char **argv, int flags,
40 struct libnvme_transport_handle **hdl)
41{
42 char *devname;
43 int ret;
44
45 ret = check_arg_dev(argc, argv);
46 if (ret)
47 return ret;
48
49 devname = argv[optind];
50
51 ret = libnvme_open(ctx, devname, flags, hdl);
52 if (ret)
53 nvme_show_err(ret, devname);
54
55 return ret;
56}
57
58void put_transport_handle(struct libnvme_transport_handle *hdl)
59{
60 libnvme_close(hdl);
61}
62
63static void setup_transport_handle(struct libnvme_global_ctx *ctx,
64 struct libnvme_transport_handle *hdl,
65 struct argconfig_commandline_options *opts)
66{
67 libnvme_transport_handle_set_submit_entry(hdl, nvme_submit_entry);
68 libnvme_transport_handle_set_submit_exit(hdl, nvme_submit_exit);
69 libnvme_transport_handle_set_decide_retry(hdl, nvme_decide_retry);
70
71#ifdef CONFIG_MI
72 if (libnvme_transport_handle_is_mi(hdl)) {
73 struct libnvme_mi_ep *ep = libnvme_transport_handle_get_mi_ep(hdl);
74 if (ep) {
75 libnvme_mi_ep_set_submit_entry(ep, nvme_mi_submit_entry);
76 libnvme_mi_ep_set_submit_exit(ep, nvme_mi_submit_exit);
77 }
78 }
79#endif
80
81 libnvme_set_dry_run(ctx, nvme_args.dry_run);
82 if (nvme_args.timeout != NVME_DEFAULT_IOCTL_TIMEOUT0)
83 libnvme_transport_handle_set_timeout(hdl, nvme_args.timeout);
84}
85
86static bool_Bool is_true(const char *val)
87{
88 return !strcmp(val, "1") ||
89 !strcasecmp(val, "true") ||
90 !strncasecmp(val, "enable", 6);
91}
92
93/*
94 * Mirror of libnvme's test sysfs root. libnvme keeps its own copy, set
95 * through libnvme_set_test_sysfs_dir(); this one covers the sysfs
96 * attributes nvme-cli reads by path rather than through libnvme.
97 */
98static char *test_sysfs_dir;
99
100static int set_test_sysfs_dir(const char *path)
101{
102 char *dup = strdup(path);
103
104 if (!dup)
105 return -ENOMEM12;
106
107 free(test_sysfs_dir);
108 test_sysfs_dir = dup;
109
110 return 0;
111}
112
113int nvme_sysfs_ctrl_path(const char *ctrl_name, char **path)
114{
115 if (asprintf(path, "%s/sys/class/nvme/%s",
116 test_sysfs_dir ? test_sysfs_dir : "", ctrl_name) < 0)
117 return -ENOMEM12;
118
119 return 0;
120}
121
122/*
123 * nvme_apply_option() - apply a single "key=value" pair to @ctx.
124 *
125 * Returns 0 on success, -EINVAL for unknown keys or missing '='.
126 */
127static int nvme_apply_option(struct libnvme_global_ctx *ctx, const char *kv)
128{
129 __cleanup_free__attribute__((cleanup(shr_freep))) char *str = NULL((void*)0);
130 char *key, *val;
131 int ret = 0;
132
133 str = strdup(kv);
134 if (!str)
135 return -ENOMEM12;
136
137 val = strchr(str, '=')_Generic (0 ? (str) : (void *) 1, const void *: (const char *
) (strchr (str, '=')), default: strchr (str, '='))
;
138 if (!val) {
139 nvme_show_error("--set-options: missing '=' in '%s'", kv)nvme_show_message(1, "--set-options: missing '=' in '%s'", kv
)
;
140 return -EINVAL22;
141 }
142 *val++ = '\0';
143 key = str;
144
145 if (!strcmp(key, "force-4k")) {
146 libnvme_set_force_4k(ctx, is_true(val));
147 } else if (!strcmp(key, "mi-probe-enabled")) {
148 libnvme_set_mi_probe_enabled(ctx, is_true(val));
149 } else if (!strcmp(key, "test-base-dir")) {
150 ret = libnvme_set_test_base_dir(ctx, val);
151 } else if (!strcmp(key, "test-sysfs-dir")) {
152 ret = libnvme_set_test_sysfs_dir(ctx, val);
153 if (!ret)
154 ret = set_test_sysfs_dir(val);
155 } else {
156 nvme_show_error("--set-options: unknown key '%s'", key)nvme_show_message(1, "--set-options: unknown key '%s'", key);
157 return -EINVAL22;
158 }
159
160 if (ret)
161 nvme_show_error("--set-options: failed to set '%s=%s': %s",nvme_show_message(1, "--set-options: failed to set '%s=%s': %s"
, key, val, libnvme_strerror(-ret))
162 key, val, libnvme_strerror(-ret))nvme_show_message(1, "--set-options: failed to set '%s=%s': %s"
, key, val, libnvme_strerror(-ret))
;
163
164 return ret;
165}
166
167static int __nvme_create_global_ctx(struct libnvme_global_ctx **pctx)
168{
169 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
170 __cleanup_free__attribute__((cleanup(shr_freep))) char *buf = NULL((void*)0);
171 const char *opt;
172 char *p;
173 int err;
174
175 ctx = libnvme_create_global_ctx();
176 if (!ctx)
6
Assuming 'ctx' is non-null
7
Taking false branch
177 return -ENOMEM12;
178
179 log_level = map_log_level(nvme_args.verbose, nvme_args.quiet);
180 libnvme_set_logging_file(ctx, stderrstderr);
181 libnvme_set_logging_level(ctx, log_level, false0, false0);
182
183 if (!nvme_args.set_options)
8
Assuming field 'set_options' is non-null
9
Taking false branch
184 goto out;
185
186 buf = strdup(nvme_args.set_options);
10
Memory is allocated
187 if (!buf)
11
Assuming 'buf' is non-null
12
Taking false branch
188 return -ENOMEM12;
189
190 p = buf;
191 while ((opt = strsep(&p, ",")) != NULL((void*)0)) {
13
Loop condition is true. Entering loop body
16
Execution continues on line 191
17
Assuming the condition is false
18
Loop condition is false. Execution jumps to the end of the function
192 if (!*opt)
14
Assuming the condition is true
15
Taking true branch
193 continue;
194 err = nvme_apply_option(ctx, opt);
195 if (err)
196 return err;
197 }
198
199out:
200 *pctx = ctx;
19
Potential leak of memory pointed to by 'opt'
201 ctx = NULL((void*)0);
202
203 return 0;
204}
205
206int nvme_create_global_ctx_hostnqn(struct libnvme_global_ctx **pctx,
207 const char *hostnqn_arg,
208 const char *hostid_arg,
209 char **hostnqn, char **hostid)
210{
211 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
212 __cleanup_free__attribute__((cleanup(shr_freep))) char *hnqn = NULL((void*)0);
213 __cleanup_free__attribute__((cleanup(shr_freep))) char *hid = NULL((void*)0);
214 int err;
215
216 err = __nvme_create_global_ctx(&ctx);
5
Calling '__nvme_create_global_ctx'
217 if (err)
218 return err;
219
220 libnvme_set_ioctl_probing(ctx, !nvme_args.no_ioctl_probing);
221
222#ifdef CONFIG_FABRICS
223 err = libnvmf_host_get_ids(ctx, hostnqn_arg, hostid_arg, &hnqn, &hid);
224 if (err)
225 return err;
226
227 libnvme_set_hostnqn(ctx, hnqn);
228 libnvme_set_hostid(ctx, hid);
229#endif
230
231 if (hostnqn) {
232 *hostnqn = hnqn;
233 hnqn = NULL((void*)0);
234 }
235 if (hostid) {
236 *hostid = hid;
237 hid = NULL((void*)0);
238 }
239
240 *pctx = ctx;
241 ctx = NULL((void*)0);
242
243 return 0;
244}
245
246int nvme_create_global_ctx(struct libnvme_global_ctx **pctx)
247{
248 return nvme_create_global_ctx_hostnqn(pctx, NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0));
4
Calling 'nvme_create_global_ctx_hostnqn'
249}
250
251int parse_and_open(struct libnvme_global_ctx **ctx,
252 struct libnvme_transport_handle **hdl, int argc, char **argv,
253 const char *desc, struct argconfig_commandline_options *opts)
254{
255 struct libnvme_transport_handle *hdl_new;
256 struct libnvme_global_ctx *ctx_new;
257 int ret;
258
259 ret = parse_args(argc, argv, desc, opts);
260 if (ret)
261 return ret;
262
263 ret = nvme_create_global_ctx(&ctx_new);
264 if (ret)
265 return ret;
266
267 ret = get_transport_handle(ctx_new, argc, argv, O_RDONLY00, &hdl_new);
268 if (ret) {
269 libnvme_free_global_ctx(ctx_new);
270 argconfig_print_help(desc, opts);
271 return -ENXIO6;
272 }
273
274 setup_transport_handle(ctx_new, hdl_new, opts);
275
276 *ctx = ctx_new;
277 *hdl = hdl_new;
278
279 return 0;
280}
281
282int open_exclusive(struct libnvme_global_ctx **ctx,
283 struct libnvme_transport_handle **hdl, int argc, char **argv,
284 int ignore_exclusive,
285 struct argconfig_commandline_options *opts)
286{
287 struct libnvme_transport_handle *hdl_new;
288 struct libnvme_global_ctx *ctx_new;
289 int flags = O_RDONLY00;
290 int ret;
291
292 if (!ignore_exclusive)
1
Assuming 'ignore_exclusive' is not equal to 0
2
Taking false branch
293 flags |= O_EXCL0200;
294
295 ret = nvme_create_global_ctx(&ctx_new);
3
Calling 'nvme_create_global_ctx'
296 if (ret)
297 return ret;
298
299 ret = get_transport_handle(ctx_new, argc, argv, flags, &hdl_new);
300 if (ret) {
301 libnvme_free_global_ctx(ctx_new);
302 return ret;
303 }
304
305 setup_transport_handle(ctx_new, hdl_new, opts);
306
307 *ctx = ctx_new;
308 *hdl = hdl_new;
309
310 return 0;
311}