Bug Summary

File:.build-ci/../src/global-ctx.c
Warning:line 139, column 3
Potential leak of memory pointed to by 'str'

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);
16
Memory is allocated
134 if (!str)
17
Assuming 'str' is non-null
18
Taking false branch
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) {
19
Assuming 'val' is null
20
Taking true branch
139 nvme_show_error("--set-options: missing '=' in '%s'", kv)nvme_show_message(1, "--set-options: missing '=' in '%s'", kv
)
;
21
Potential leak of memory pointed to by 'str'
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);
187 if (!buf)
10
Assuming 'buf' is non-null
11
Taking false branch
188 return -ENOMEM12;
189
190 p = buf;
191 while ((opt = strsep(&p, ",")) != NULL((void*)0)) {
12
Loop condition is true. Entering loop body
192 if (!*opt)
13
Assuming the condition is false
14
Taking false branch
193 continue;
194 err = nvme_apply_option(ctx, opt);
15
Calling 'nvme_apply_option'
195 if (err)
196 return err;
197 }
198
199out:
200 *pctx = ctx;
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}