Bug Summary

File:.build-ci/../tests/cli/libmock_nvme.c
Warning:line 953, column 12
Potential leak of memory pointed to by 'payload'

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 libmock_nvme.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 -fhalf-no-semantic-interposition -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/cli/libmock_nvme.so.p -I tests/cli -I ../tests/cli -I . -I .. -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-11-02-081605-642-1 -x c ../tests/cli/libmock_nvme.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * This file is part of nvme-cli.
4 *
5 * Copyright (c) 2026 SUSE LLC
6 *
7 * Authors: Daniel Wagner <dwagner@suse.com>
8 */
9
10/*
11 * LD_PRELOAD shim for the CLI-driven, hardware-free tests under tests/cli/.
12 *
13 * Intercepts open()/write()/ioctl()/close() on /dev/nvme-fabrics,
14 * /dev/nvme<N> (controller char device), and /dev/nvme<N>n<M> (namespace
15 * char device). This lets any nvme-cli command run against a fake target,
16 * with no kernel nvme-fabrics module, no real hardware, and no root.
17 *
18 * Also intercepts the host network queries the default --nr-io-queues
19 * computation makes: the NETLINK_ROUTE egress route lookup, the
20 * ETHTOOL_GCHANNELS ioctl() and get_nprocs(). So the test, not the host
21 * running it, decides the NIC and CPU setup.
22 *
23 * Every intercepted write() (fabrics connect args) or ioctl() goes over a
24 * Unix socket to a Python IPC server. Admin and I/O passthru commands are
25 * decoded here, so the server sees the command fields. Any other ioctl()
26 * is forwarded raw: its decoded request number and argument bytes. So is
27 * getpass(), so a test can answer password prompts. The server sends back
28 * the response. This way, the Python test file controls the whole
29 * scenario, not this shim.
30 */
31
32/*
33 * This file defines open()/open64()/openat()/openat64()/fstat()/fstat64()
34 * as distinct, separately-interposed symbols, so _FILE_OFFSET_BITS must
35 * stay undefined here: otherwise glibc's LFS headers would "#define open
36 * open64" etc. and silently rename this file's own open() definition into
37 * a second open64(), colliding with the explicit one. On the 32-bit
38 * architectures Debian/Ubuntu have migrated to 64-bit time_t (e.g. armhf),
39 * the cross-gcc implicitly sets _TIME_BITS=64, which glibc's headers
40 * refuse to pair with an unset/non-64 _FILE_OFFSET_BITS -- so that has to
41 * come undefined too, for the same "let this TU pick its own copy of these
42 * macros, independent of the project-wide -D_FILE_OFFSET_BITS=64" reason.
43 */
44#undef _FILE_OFFSET_BITS
45#undef _TIME_BITS
46#ifndef _GNU_SOURCE1
47#define _GNU_SOURCE1
48#endif
49
50#include <arpa/inet.h>
51#include <dlfcn.h>
52#include <endian.h>
53#include <errno(*__errno_location ()).h>
54#include <fcntl.h>
55#include <linux1/ethtool.h>
56#include <linux1/netlink.h>
57#include <linux1/rtnetlink.h>
58#include <linux1/sockios.h>
59#include <linux1/types.h>
60#include <net/if.h>
61#include <stdarg.h>
62#include <stdbool.h>
63#include <stdint.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67#include <sys/ioctl.h>
68#include <sys/socket.h>
69#include <sys/stat.h>
70#include <sys/sysinfo.h>
71#include <sys/types.h>
72#include <sys/un.h>
73#include <unistd.h>
74
75#include <shared/cleanup-util.h>
76#include <shared/compiler-attributes-util.h>
77
78struct linux_passthru_cmd32 {
79 __u8 opcode;
80 __u8 flags;
81 __u16 rsvd1;
82 __u32 nsid;
83 __u32 cdw2;
84 __u32 cdw3;
85 __u64 metadata;
86 __u64 addr;
87 __u32 metadata_len;
88 __u32 data_len;
89 __u32 cdw10;
90 __u32 cdw11;
91 __u32 cdw12;
92 __u32 cdw13;
93 __u32 cdw14;
94 __u32 cdw15;
95 __u32 timeout_ms;
96 __u32 result;
97};
98
99struct linux_passthru_cmd64 {
100 __u8 opcode;
101 __u8 flags;
102 __u16 rsvd1;
103 __u32 nsid;
104 __u32 cdw2;
105 __u32 cdw3;
106 __u64 metadata;
107 __u64 addr;
108 __u32 metadata_len;
109 __u32 data_len;
110 __u32 cdw10;
111 __u32 cdw11;
112 __u32 cdw12;
113 __u32 cdw13;
114 __u32 cdw14;
115 __u32 cdw15;
116 __u32 timeout_ms;
117 __u32 rsvd2;
118 __u64 result;
119};
120
121#define LIBNVME_IOCTL_ADMIN_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x41)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
_IOWR('N', 0x41, struct linux_passthru_cmd32)(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x41)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
122#define LIBNVME_IOCTL_IO_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x43)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
_IOWR('N', 0x43, struct linux_passthru_cmd32)(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x43)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
123#define LIBNVME_IOCTL_ADMIN64_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x47)) << 0) | ((((sizeof(struct linux_passthru_cmd64
)))) << ((0 +8)+8)))
_IOWR('N', 0x47, struct linux_passthru_cmd64)(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x47)) << 0) | ((((sizeof(struct linux_passthru_cmd64
)))) << ((0 +8)+8)))
124#define LIBNVME_IOCTL_IO64_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x48)) << 0) | ((((sizeof(struct linux_passthru_cmd64
)))) << ((0 +8)+8)))
_IOWR('N', 0x48, struct linux_passthru_cmd64)(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x48)) << 0) | ((((sizeof(struct linux_passthru_cmd64
)))) << ((0 +8)+8)))
125
126enum ipc_type {
127 IPC_TYPE_WRITE = 1, /* write() on /dev/nvme-fabrics */
128 IPC_TYPE_IOCTL = 2, /* admin or I/O passthru ioctl() */
129 IPC_TYPE_RAW_IOCTL = 3, /* other ioctl() on a mocked device */
130 IPC_TYPE_GETPASS = 4, /* getpass(), payload is the prompt */
131 IPC_TYPE_ROUTE = 5, /* route lookup, "daddr\0saddr" */
132 IPC_TYPE_NIC_QUEUES = 6, /* ETHTOOL_GCHANNELS, the ifname */
133 IPC_TYPE_NPROCS = 7, /* get_nprocs() */
134};
135
136/* Direction of a raw ioctl, independent of the arch's _IOC_* values. */
137enum ipc_ioc_dir {
138 IPC_IOC_WRITE = 1 << 0, /* the caller passes data in */
139 IPC_IOC_READ = 1 << 1, /* the caller gets data back */
140};
141
142/*
143 * What a passthru request carries. A command which transfers data to the
144 * controller sends its data, then its metadata, as the payload.
145 */
146enum ipc_cmd_flags {
147 IPC_CMD_ADMIN = 1 << 0, /* admin, not I/O, queue */
148 IPC_CMD_DATA = 1 << 1, /* payload has the data */
149 IPC_CMD_METADATA = 1 << 2, /* payload has the metadata */
150};
151
152/* Wire format shared with the Python IPC server -- keep both sides in sync. */
153struct __attribute__((packed)) ipc_request {
154 uint32_t type; /* enum ipc_type */
155 uint32_t fd; /* instance number for IOCTL */
156 uint32_t data_len; /* payload length (follows immediately) */
157 uint32_t request; /* ioctl request code */
158 uint8_t opcode; /* nvme opcode */
159 uint8_t rsvd[3];
160 uint32_t nsid;
161 uint32_t cdw10;
162 uint32_t cdw11;
163 uint32_t cdw12;
164 uint32_t cdw13;
165 uint32_t cdw14;
166 uint32_t cdw15;
167 uint32_t req_len; /* command data length */
168 /* raw ioctl only: the decoded request number */
169 uint8_t ioc_dir; /* enum ipc_ioc_dir */
170 uint8_t ioc_type;
171 uint8_t ioc_nr;
172 uint8_t big_endian; /* byte order of the payload */
173 uint32_t ioc_size;
174 /* passthru only: the rest of the command */
175 uint8_t flags;
176 uint8_t cmd_flags; /* enum ipc_cmd_flags */
177 uint8_t rsvd2[2];
178 uint32_t cdw2;
179 uint32_t cdw3;
180 uint32_t metadata_len;
181 uint32_t timeout_ms;
182};
183
184struct __attribute__((packed)) ipc_response {
185 int32_t status; /* 0 for success, -1 for a real syscall/errno failure */
186 int32_t errno_val; /* errno to return on error (status == -1 only) */
187 int32_t sc_status; /* NVMe completion status */
188 uint64_t result; /* cmd->result */
189 uint32_t data_len; /* payload length (follows immediately) */
190};
191
192#ifndef MOCK_FUZZ
193/*
194 * This process may be running as real target-arch code (a cross build
195 * under qemu-user), while nvme_mock_ipc.py on the other end of the socket
196 * is always native host-arch Python. Those are only the same "native" byte
197 * order on a non-cross run, so the wire format is explicit little-endian
198 * (matching nvme_mock_ipc.py's "<" struct formats) rather than whatever
199 * this process's native order happens to be.
200 */
201static void ipc_request_encode(struct ipc_request *r)
202{
203 r->type = htole32(r->type)__uint32_identity (r->type);
204 r->fd = htole32(r->fd)__uint32_identity (r->fd);
205 r->data_len = htole32(r->data_len)__uint32_identity (r->data_len);
206 r->request = htole32(r->request)__uint32_identity (r->request);
207 r->nsid = htole32(r->nsid)__uint32_identity (r->nsid);
208 r->cdw10 = htole32(r->cdw10)__uint32_identity (r->cdw10);
209 r->cdw11 = htole32(r->cdw11)__uint32_identity (r->cdw11);
210 r->cdw12 = htole32(r->cdw12)__uint32_identity (r->cdw12);
211 r->cdw13 = htole32(r->cdw13)__uint32_identity (r->cdw13);
212 r->cdw14 = htole32(r->cdw14)__uint32_identity (r->cdw14);
213 r->cdw15 = htole32(r->cdw15)__uint32_identity (r->cdw15);
214 r->req_len = htole32(r->req_len)__uint32_identity (r->req_len);
215 r->ioc_size = htole32(r->ioc_size)__uint32_identity (r->ioc_size);
216 r->cdw2 = htole32(r->cdw2)__uint32_identity (r->cdw2);
217 r->cdw3 = htole32(r->cdw3)__uint32_identity (r->cdw3);
218 r->metadata_len = htole32(r->metadata_len)__uint32_identity (r->metadata_len);
219 r->timeout_ms = htole32(r->timeout_ms)__uint32_identity (r->timeout_ms);
220}
221
222static void ipc_response_decode(struct ipc_response *r)
223{
224 r->status = (int32_t)le32toh((uint32_t)r->status)__uint32_identity ((uint32_t)r->status);
225 r->errno_val = (int32_t)le32toh((uint32_t)r->errno_val)__uint32_identity ((uint32_t)r->errno_val);
226 r->sc_status = (int32_t)le32toh((uint32_t)r->sc_status)__uint32_identity ((uint32_t)r->sc_status);
227 r->result = le64toh(r->result)__uint64_identity (r->result);
228 r->data_len = le32toh(r->data_len)__uint32_identity (r->data_len);
229}
230#endif
231
232typedef int (*orig_open_t)(const char *pathname, int flags, ...);
233typedef int (*orig_open64_t)(const char *pathname, int flags, ...);
234typedef int (*orig_openat_t)(int dirfd, const char *pathname, int flags, ...);
235typedef int (*orig_openat64_t)(int dirfd, const char *pathname, int flags, ...);
236typedef ssize_t (*orig_write_t)(int fd, const void *buf, size_t count);
237typedef ssize_t (*orig_read_t)(int fd, void *buf, size_t count);
238typedef int (*orig_ioctl_t)(int fd, unsigned long request, ...);
239typedef int (*orig_close_t)(int fd);
240typedef int (*orig_fstat_t)(int fd, struct stat *buf);
241typedef int (*orig_fstat64_t)(int fd, struct stat64 *buf);
242typedef int (*orig_fstat64_time64_t)(int fd, void *buf);
243typedef char *(*orig_getpass_t)(const char *prompt);
244typedef int (*orig_socket_t)(int domain, int type, int protocol);
245typedef ssize_t (*orig_sendmsg_t)(int fd, const struct msghdr *msg, int flags);
246typedef ssize_t (*orig_recvmsg_t)(int fd, struct msghdr *msg, int flags);
247typedef char *(*orig_if_indextoname_t)(unsigned int ifindex, char *ifname);
248typedef int (*orig_get_nprocs_t)(void);
249
250static orig_open_t orig_open;
251static orig_open64_t orig_open64;
252static orig_openat_t orig_openat;
253static orig_openat64_t orig_openat64;
254static orig_write_t orig_write;
255#ifndef MOCK_FUZZ
256static orig_read_t orig_read;
257#endif
258static orig_ioctl_t orig_ioctl;
259static orig_ioctl_t orig_ioctl_time64;
260static orig_close_t orig_close;
261static orig_fstat_t orig_fstat;
262static orig_fstat64_t orig_fstat64;
263static orig_fstat64_time64_t orig_fstat64_time64;
264static orig_getpass_t orig_getpass;
265static orig_socket_t orig_socket;
266static orig_sendmsg_t orig_sendmsg;
267static orig_sendmsg_t orig_sendmsg64;
268static orig_recvmsg_t orig_recvmsg;
269static orig_recvmsg_t orig_recvmsg64;
270static orig_if_indextoname_t orig_if_indextoname;
271static orig_get_nprocs_t orig_get_nprocs;
272
273static int mock_fabrics_fd = -1;
274static int mock_fabrics_manager_fd = -1;
275static bool_Bool mock_debug;
276
277#define MAX_MOCK_CTRLS16 16
278static struct {
279 int fd;
280 int instance;
281 bool_Bool is_block;
282} mock_ctrl_fds[MAX_MOCK_CTRLS16];
283static int num_mock_ctrls;
284
285/*
286 * The route lookup reply: the egress interface the IPC server picked,
287 * or the errno it failed with. The interface gets a made-up ifindex,
288 * which if_indextoname() maps back to the name.
289 */
290#define MOCK_IFINDEX0x4d4f434b 0x4d4f434b
291static int mock_netlink_fd = -1;
292static struct {
293 bool_Bool pending;
294 struct nlmsghdr req;
295 unsigned char family;
296 int error;
297} mock_route_reply;
298static char mock_ifname[IF_NAMESIZE16];
299
300#define mock_dbg(fmt, ...)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " fmt, ...
); } while (0)
\
301 do { \
302 if (mock_debug) \
303 fprintf(stderrstderr, "[MOCK_DEBUG] " fmt, ##__VA_ARGS__); \
304 } while (0)
305
306static void __shr_constructor__attribute__((constructor)) mock_init(void)
307{
308 mock_debug = !!getenv("MOCK_DEBUG");
309 mock_dbg("libmock_nvme loaded, sizeof(ipc_request)=%zu, sizeof(ipc_response)=%zu\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "libmock_nvme loaded, sizeof(ipc_request)=%zu, sizeof(ipc_response)=%zu\n"
, sizeof(struct ipc_request), sizeof(struct ipc_response)); }
while (0)
310 sizeof(struct ipc_request), sizeof(struct ipc_response))do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "libmock_nvme loaded, sizeof(ipc_request)=%zu, sizeof(ipc_response)=%zu\n"
, sizeof(struct ipc_request), sizeof(struct ipc_response)); }
while (0)
;
311}
312
313static void init_orig_functions(void)
314{
315 if (orig_open)
316 return;
317
318 orig_open = (orig_open_t)dlsym(RTLD_NEXT((void *) -1l), "open");
319 orig_open64 = (orig_open64_t)dlsym(RTLD_NEXT((void *) -1l), "open64");
320 orig_openat = (orig_openat_t)dlsym(RTLD_NEXT((void *) -1l), "openat");
321 orig_openat64 = (orig_openat64_t)dlsym(RTLD_NEXT((void *) -1l), "openat64");
322 orig_write = (orig_write_t)dlsym(RTLD_NEXT((void *) -1l), "write");
323#ifndef MOCK_FUZZ
324 orig_read = (orig_read_t)dlsym(RTLD_NEXT((void *) -1l), "read");
325#endif
326 orig_ioctl = (orig_ioctl_t)dlsym(RTLD_NEXT((void *) -1l), "ioctl");
327 orig_ioctl_time64 = (orig_ioctl_t)dlsym(RTLD_NEXT((void *) -1l), "__ioctl_time64");
328 orig_close = (orig_close_t)dlsym(RTLD_NEXT((void *) -1l), "close");
329 orig_fstat = (orig_fstat_t)dlsym(RTLD_NEXT((void *) -1l), "fstat");
330 orig_fstat64 = (orig_fstat64_t)dlsym(RTLD_NEXT((void *) -1l), "fstat64");
331 orig_fstat64_time64 = (orig_fstat64_time64_t)dlsym(RTLD_NEXT((void *) -1l), "__fstat64_time64");
332 orig_getpass = (orig_getpass_t)dlsym(RTLD_NEXT((void *) -1l), "getpass");
333 orig_socket = (orig_socket_t)dlsym(RTLD_NEXT((void *) -1l), "socket");
334 orig_sendmsg = (orig_sendmsg_t)dlsym(RTLD_NEXT((void *) -1l), "sendmsg");
335 orig_sendmsg64 = (orig_sendmsg_t)dlsym(RTLD_NEXT((void *) -1l), "__sendmsg64");
336 orig_recvmsg = (orig_recvmsg_t)dlsym(RTLD_NEXT((void *) -1l), "recvmsg");
337 orig_recvmsg64 = (orig_recvmsg_t)dlsym(RTLD_NEXT((void *) -1l), "__recvmsg64");
338 orig_if_indextoname = (orig_if_indextoname_t)dlsym(RTLD_NEXT((void *) -1l),
339 "if_indextoname");
340 orig_get_nprocs = (orig_get_nprocs_t)dlsym(RTLD_NEXT((void *) -1l), "get_nprocs");
341}
342
343/*
344 * Built into a fuzz target (tests/fuzz/fuzz-device.c), the shim has no
345 * IPC server: the fuzz input answers every command instead. A "socket"
346 * is a /dev/null fd, so the callers can close it as usual.
347 *
348 * Both builds define each function once, at the same line, and keep
349 * MOCK_FUZZ inside the body: gcovr merges the coverage of both and
350 * fails on a function it finds on two lines.
351 */
352static int connect_ipc_socket(void)
353{
354#ifdef MOCK_FUZZ
355 return orig_open("/dev/null", O_RDWR02);
356#else
357 const char *sock_path = getenv("MOCK_IPC_SOCK");
358 struct sockaddr_un addr;
359 int fd;
360
361 if (!sock_path) {
362 mock_dbg("MOCK_IPC_SOCK env var not set!\n")do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "MOCK_IPC_SOCK env var not set!\n"
); } while (0)
;
363 return -1;
364 }
365
366 fd = orig_socket(AF_UNIX1, SOCK_STREAMSOCK_STREAM, 0);
367 if (fd < 0) {
368 mock_dbg("failed to create socket: %s\n", strerror(errno))do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "failed to create socket: %s\n"
, strerror((*__errno_location ()))); } while (0)
;
369 return -1;
370 }
371
372 memset(&addr, 0, sizeof(addr));
373 addr.sun_family = AF_UNIX1;
374 strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
375 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
376 mock_dbg("connect failed to '%s': %s\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "connect failed to '%s': %s\n"
, sock_path, strerror((*__errno_location ()))); } while (0)
377 sock_path, strerror(errno))do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "connect failed to '%s': %s\n"
, sock_path, strerror((*__errno_location ()))); } while (0)
;
378 orig_close(fd);
379 return -1;
380 }
381
382 return fd;
383#endif
384}
385
386const char *default_opts =
387 "instance,cntlid,concat,ctrl_loss_tmo,data_digest,"
388 "dhchap_ctrl_secret,dhchap_secret,disable_sqflow,"
389 "discovery,duplicate_connect,fast_io_fail_tmo,"
390 "hdr_digest,host_iface,host_traddr,hostid,hostnqn,"
391 "keep_alive_tmo,keyring,nqn,nr_io_queues,"
392 "nr_poll_queues,nr_write_queues,queue_size,"
393 "reconnect_delay,tls,tls_key,tos,traddr,transport,"
394 "trsvcid\n";
395
396static int handle_open_intercept(const char *pathname, int flags)
397{
398 int instance, real_fd, sv[2];
399 bool_Bool is_block;
400 char *endp;
401
402 if (!pathname)
403 return -2;
404
405 if (!strcmp(pathname, "/dev/nvme-fabrics")) {
406 if (socketpair(AF_UNIX1, SOCK_STREAMSOCK_STREAM, 0, sv) < 0)
407 return -1;
408
409 mock_fabrics_manager_fd = sv[0];
410 mock_fabrics_fd = sv[1];
411
412 mock_dbg("intercepted /dev/nvme-fabrics open\n")do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "intercepted /dev/nvme-fabrics open\n"
); } while (0)
;
413
414 if ((flags & O_ACCMODE0003) == O_RDONLY00) {
415 const char *opts = getenv("MOCK_SUPPORTED_OPTIONS");
416
417 if (!opts)
418 opts = default_opts;
419 orig_write(mock_fabrics_manager_fd, opts, strlen(opts));
420 orig_close(mock_fabrics_manager_fd);
421 mock_fabrics_manager_fd = -1;
422 }
423
424 return mock_fabrics_fd;
425 }
426
427 if (strncmp(pathname, "/dev/nvme", 9) ||
428 pathname[9] < '0' ||
429 pathname[9] > '9')
430 return -2;
431
432 instance = strtol(pathname + 9, &endp, 10);
433 is_block = (*endp == 'n');
434 if (is_block) {
435 char *nsp;
436
437 strtol(endp + 1, &nsp, 10);
438 if (nsp == endp + 1 || *nsp != '\0')
439 return -2;
440 } else if (*endp != '\0') {
441 return -2;
442 }
443
444 real_fd = orig_open("/dev/null", flags);
445 if (real_fd >= 0) {
446 if (num_mock_ctrls < MAX_MOCK_CTRLS16) {
447 mock_ctrl_fds[num_mock_ctrls].fd = real_fd;
448 mock_ctrl_fds[num_mock_ctrls].instance = instance;
449 mock_ctrl_fds[num_mock_ctrls].is_block = is_block;
450 num_mock_ctrls++;
451 }
452 mock_dbg("intercepted /dev/nvme%d open (mapped to fd %d)\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "intercepted /dev/nvme%d open (mapped to fd %d)\n"
, instance, real_fd); } while (0)
453 instance, real_fd)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "intercepted /dev/nvme%d open (mapped to fd %d)\n"
, instance, real_fd); } while (0)
;
454 }
455
456 return real_fd;
457}
458
459static mode_t open_mode_arg(int flags, va_list args)
460{
461 if (flags & (O_CREAT0100 | O_TMPFILE(020000000 | 0200000)))
462 return va_arg(args, mode_t)__builtin_va_arg(args, mode_t);
463
464 return 0;
465}
466
467int open(const char *pathname, int flags, ...)
468{
469 va_list args;
470 mode_t mode;
471 int fd;
472
473 init_orig_functions();
474 fd = handle_open_intercept(pathname, flags);
475 if (fd != -2)
476 return fd;
477
478 va_start(args, flags)__builtin_va_start(args, flags);
479 mode = open_mode_arg(flags, args);
480 va_end(args)__builtin_va_end(args);
481
482 return orig_open(pathname, flags, mode);
483}
484
485int open64(const char *pathname, int flags, ...)
486{
487 va_list args;
488 mode_t mode;
489 int fd;
490
491 init_orig_functions();
492 fd = handle_open_intercept(pathname, flags);
493 if (fd != -2)
494 return fd;
495
496 va_start(args, flags)__builtin_va_start(args, flags);
497 mode = open_mode_arg(flags, args);
498 va_end(args)__builtin_va_end(args);
499
500 return orig_open64(pathname, flags, mode);
501}
502
503int openat(int dirfd, const char *pathname, int flags, ...)
504{
505 va_list args;
506 mode_t mode;
507 int fd;
508
509 init_orig_functions();
510 fd = handle_open_intercept(pathname, flags);
511 if (fd != -2)
512 return fd;
513
514 va_start(args, flags)__builtin_va_start(args, flags);
515 mode = open_mode_arg(flags, args);
516 va_end(args)__builtin_va_end(args);
517
518 return orig_openat(dirfd, pathname, flags, mode);
519}
520
521int openat64(int dirfd, const char *pathname, int flags, ...)
522{
523 va_list args;
524 mode_t mode;
525 int fd;
526
527 init_orig_functions();
528 fd = handle_open_intercept(pathname, flags);
529 if (fd != -2)
530 return fd;
531
532 va_start(args, flags)__builtin_va_start(args, flags);
533 mode = open_mode_arg(flags, args);
534 va_end(args)__builtin_va_end(args);
535
536 return orig_openat64(dirfd, pathname, flags, mode);
537}
538
539int __open_2(const char *pathname, int flags)
540{
541 int fd;
542
543 init_orig_functions();
544 fd = handle_open_intercept(pathname, flags);
545 if (fd != -2)
546 return fd;
547
548 return orig_open(pathname, flags, 0);
549}
550
551int __open64_2(const char *pathname, int flags)
552{
553 int fd;
554
555 init_orig_functions();
556 fd = handle_open_intercept(pathname, flags);
557 if (fd != -2)
558 return fd;
559
560 return orig_open64(pathname, flags, 0);
561}
562
563int __openat_2(int dirfd, const char *pathname, int flags)
564{
565 int fd;
566
567 init_orig_functions();
568 fd = handle_open_intercept(pathname, flags);
569 if (fd != -2)
570 return fd;
571
572 return orig_openat(dirfd, pathname, flags, 0);
573}
574
575int __openat64_2(int dirfd, const char *pathname, int flags)
576{
577 int fd;
578
579 init_orig_functions();
580 fd = handle_open_intercept(pathname, flags);
581 if (fd != -2)
582 return fd;
583
584 return orig_openat64(dirfd, pathname, flags, 0);
585}
586
587/*
588 * Bits 1:0 of an opcode give the direction of its data transfer. Like
589 * the kernel, treat a bidirectional command as a write.
590 */
591static bool_Bool opcode_is_write(uint8_t opcode)
592{
593 return opcode & 1;
594}
595
596#ifdef MOCK_FUZZ
597/*
598 * The kernel rejects a transfer larger than the controller takes. And a
599 * fuzzed log length must not have the mock serve, and nvme-cli print,
600 * gigabytes: the data of all commands of a run is limited too. A
601 * megabyte of log entries already prints as tens of megabytes of text,
602 * which takes seconds with the sanitizers. FUZZ_DATA_LIMIT sets another
603 * limit in bytes: most of the data structures fit a page, and with less
604 * data an input runs faster.
605 */
606#define FUZZ_MAX_TRANSFER (2 * 1024 * 1024)
607#define FUZZ_MAX_TOTAL (1 * 1024 * 1024)
608
609static const uint8_t *fuzz_data;
610static size_t fuzz_size;
611static size_t fuzz_total;
612static size_t fuzz_limit;
613
614static size_t fuzz_data_limit(void)
615{
616 unsigned long long limit;
617 const char *env;
618 char *end;
619
620 if (fuzz_limit)
621 return fuzz_limit;
622 fuzz_limit = FUZZ_MAX_TOTAL;
623 env = getenv("FUZZ_DATA_LIMIT");
624 if (!env)
625 return fuzz_limit;
626 errno(*__errno_location ()) = 0;
627 limit = strtoull(env, &end, 10);
628 if (errno(*__errno_location ()) || end == env || *end || !limit ||
629 (size_t)limit != limit) {
630 fprintf(stderrstderr, "FUZZ_DATA_LIMIT=%s: not a size in bytes\n",
631 env);
632 exit(EXIT_FAILURE1);
633 }
634 fuzz_limit = limit;
635 return fuzz_limit;
636}
637
638/* The bytes the following commands get their answers from */
639void mock_fuzz_input(const uint8_t *data, size_t size)
640{
641 fuzz_data = data;
642 fuzz_size = size;
643 fuzz_total = 0;
644}
645
646/* The next bytes of the input, zeros once it is used up */
647static void fuzz_read(void *buf, size_t len)
648{
649 size_t n = len < fuzz_size ? len : fuzz_size;
650
651 memcpy(buf, fuzz_data, n);
652 memset((uint8_t *)buf + n, 0, len - n);
653 fuzz_data += n;
654 fuzz_size -= n;
655}
656
657/*
658 * Answer a command from the fuzz input: a control byte, the result
659 * dword, and then the data the command reads, none for a write. Most
660 * control bytes mean success, the rest an NVMe status (from the next two
661 * bytes) or a failed ioctl(). Everything that is no device command
662 * fails, so the real function takes over where there is one.
663 */
664static void *fuzz_ipc_call(struct ipc_request *req,
665 struct ipc_response *resp)
666{
667 uint32_t result;
668 uint8_t ctl;
669 uint16_t sc;
670 void *buf;
671
672 memset(resp, 0, sizeof(*resp));
673
674 switch (req->type) {
675 case IPC_TYPE_IOCTL:
676 if (!opcode_is_write(req->opcode))
677 resp->data_len = req->req_len;
678 break;
679 case IPC_TYPE_RAW_IOCTL:
680 if (req->ioc_dir & IPC_IOC_READ)
681 resp->data_len = req->ioc_size;
682 break;
683 default:
684 resp->status = -1;
685 resp->errno_val = EOPNOTSUPP95;
686 return NULL((void*)0);
687 }
688
689 if (resp->data_len > FUZZ_MAX_TRANSFER) {
690 resp->status = -1;
691 resp->errno_val = EINVAL22;
692 return NULL((void*)0);
693 }
694 if (resp->data_len > fuzz_data_limit() - fuzz_total) {
695 resp->status = -1;
696 resp->errno_val = EIO5;
697 return NULL((void*)0);
698 }
699 fuzz_total += resp->data_len;
700
701 fuzz_read(&ctl, sizeof(ctl));
702 /* The fuzz input keeps a 32 bit result. */
703 fuzz_read(&result, sizeof(result));
704 resp->result = le32toh(result)__uint32_identity (result);
705 if (ctl >= 0xf8) {
706 resp->status = -1;
707 resp->errno_val = EIO5;
708 return NULL((void*)0);
709 }
710 if (ctl >= 0xe0) {
711 fuzz_read(&sc, sizeof(sc));
712 resp->sc_status = le16toh(sc)__uint16_identity (sc) & 0x7fff;
713 return NULL((void*)0);
714 }
715
716 if (!resp->data_len)
717 return NULL((void*)0);
718 buf = malloc(resp->data_len);
719 if (!buf) {
720 resp->status = -1;
721 resp->errno_val = ENOMEM12;
722 return NULL((void*)0);
723 }
724 fuzz_read(buf, resp->data_len);
725
726 return buf;
727}
728#else
729static bool_Bool read_full(int fd, void *buf, size_t len)
730{
731 size_t total_read = 0;
732
733 while (total_read < len) {
734 ssize_t n = orig_read(fd, (char *)buf + total_read,
735 len - total_read);
736 if (n < 0 && errno(*__errno_location ()) == EINTR4)
737 continue;
738 if (n <= 0)
739 return false0;
740 total_read += n;
741 }
742
743 return true1;
744}
745
746static bool_Bool write_full(int fd, const void *buf, size_t len)
747{
748 size_t total_written = 0;
749
750 while (total_written < len) {
751 ssize_t n = orig_write(fd, (const char *)buf + total_written,
752 len - total_written);
753 if (n < 0 && errno(*__errno_location ()) == EINTR4)
754 continue;
755 if (n <= 0)
756 return false0;
757 total_written += n;
758 }
759
760 return true1;
761}
762
763static void *read_ipc_response(int ipc_fd, struct ipc_response *resp)
764{
765 char *payload;
766
767 memset(resp, 0, sizeof(*resp));
768 if (!read_full(ipc_fd, resp, sizeof(*resp))) {
769 mock_dbg("failed to read full IPC response header\n")do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "failed to read full IPC response header\n"
); } while (0)
;
770 resp->status = -1;
771 resp->errno_val = EIO5;
772 return NULL((void*)0);
773 }
774 ipc_response_decode(resp);
775
776 if (!resp->data_len)
777 return NULL((void*)0);
778
779 payload = malloc(resp->data_len);
780 if (!payload)
781 return NULL((void*)0);
782
783 if (!read_full(ipc_fd, payload, resp->data_len)) {
784 mock_dbg("failed to read full IPC response payload\n")do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "failed to read full IPC response payload\n"
); } while (0)
;
785 free(payload);
786 resp->status = -1;
787 resp->errno_val = EIO5;
788 return NULL((void*)0);
789 }
790
791 return payload;
792}
793
794#endif
795
796/*
797 * Send a request and its data_len bytes of data, and read the response.
798 * A failed send is reported like a failed read.
799 */
800static void *ipc_call(int ipc_fd, struct ipc_request *req, const void *data,
801 struct ipc_response *resp)
802{
803#ifdef MOCK_FUZZ
804 (void)ipc_fd;
805 (void)data;
806 return fuzz_ipc_call(req, resp);
807#else
808 size_t len = req->data_len;
809
810 ipc_request_encode(req);
811 if (!write_full(ipc_fd, req, sizeof(*req)) ||
812 (len && !write_full(ipc_fd, data, len))) {
813 mock_dbg("failed to send IPC request\n")do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "failed to send IPC request\n"
); } while (0)
;
814 memset(resp, 0, sizeof(*resp));
815 resp->status = -1;
816 resp->errno_val = EIO5;
817 return NULL((void*)0);
818 }
819
820 return read_ipc_response(ipc_fd, resp);
821#endif
822}
823
824ssize_t write(int fd, const void *buf, size_t count)
825{
826 struct ipc_response resp;
827 char *resp_data;
828 int ipc_fd;
829
830 init_orig_functions();
831
832 if (fd != mock_fabrics_fd)
833 return orig_write(fd, buf, count);
834
835 mock_dbg("intercepted connect args on fabrics fd %d\n", fd)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "intercepted connect args on fabrics fd %d\n"
, fd); } while (0)
;
836
837 ipc_fd = connect_ipc_socket();
838 if (ipc_fd < 0) {
839 mock_dbg("could not connect to IPC socket\n")do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "could not connect to IPC socket\n"
); } while (0)
;
840 return orig_write(fd, buf, count);
841 }
842
843 struct ipc_request req = {
844 .type = IPC_TYPE_WRITE,
845 .fd = fd,
846 .data_len = count,
847 };
848
849 resp_data = ipc_call(ipc_fd, &req, buf, &resp);
850 mock_dbg("connect IPC response status %d, data_len %u\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "connect IPC response status %d, data_len %u\n"
, resp.status, resp.data_len); } while (0)
851 resp.status, resp.data_len)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "connect IPC response status %d, data_len %u\n"
, resp.status, resp.data_len); } while (0)
;
852
853 if (resp.status == -1) {
854 free(resp_data);
855 orig_close(ipc_fd);
856 errno(*__errno_location ()) = resp.errno_val;
857 return -1;
858 }
859
860 if (resp_data && mock_fabrics_manager_fd >= 0)
861 orig_write(mock_fabrics_manager_fd, resp_data, resp.data_len);
862 free(resp_data);
863 orig_close(ipc_fd);
864
865 return count;
866}
867
868static int mock_ctrl_instance(int fd)
869{
870 for (int i = 0; i < num_mock_ctrls; i++)
871 if (mock_ctrl_fds[i].fd == fd)
872 return mock_ctrl_fds[i].instance;
873
874 return -1;
875}
876
877static bool_Bool mock_ctrl_is_block(int fd, bool_Bool *is_block)
878{
879 for (int i = 0; i < num_mock_ctrls; i++) {
880 if (mock_ctrl_fds[i].fd == fd) {
881 *is_block = mock_ctrl_fds[i].is_block;
882 return true1;
883 }
884 }
885
886 return false0;
887}
888
889static int handle_passthru_ioctl(int instance, unsigned long request,
890 struct linux_passthru_cmd64 *cmd)
891{
892 bool_Bool admin = request == LIBNVME_IOCTL_ADMIN_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x41)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
||
893 request == LIBNVME_IOCTL_ADMIN64_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x47)) << 0) | ((((sizeof(struct linux_passthru_cmd64
)))) << ((0 +8)+8)))
;
894 bool_Bool write = opcode_is_write(cmd->opcode);
895 uint32_t data_len = 0, metadata_len = 0;
896 __cleanup_free__attribute__((cleanup(shr_freep))) void *payload = NULL((void*)0);
897 struct ipc_response resp;
898 void *resp_data;
899 int ipc_fd;
900
901 mock_dbg("ioctl intercepted on instance %d (request 0x%lx)\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl intercepted on instance %d (request 0x%lx)\n"
, instance, request); } while (0)
6
Assuming 'mock_debug' is false
7
Taking false branch
8
Loop condition is false. Exiting loop
902 instance, request)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl intercepted on instance %d (request 0x%lx)\n"
, instance, request); } while (0)
;
903
904
905 ipc_fd = connect_ipc_socket();
906 if (ipc_fd < 0) {
9
Assuming 'ipc_fd' is >= 0
907 mock_dbg("ioctl could not connect to IPC socket\n")do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl could not connect to IPC socket\n"
); } while (0)
;
908 return -2;
909 }
910
911 if (write && cmd->addr)
10
Assuming 'write' is true
11
Assuming field 'addr' is not equal to 0
12
Taking true branch
912 data_len = cmd->data_len;
913 if (write
12.1
'write' is true
&& cmd->metadata)
13
Assuming field 'metadata' is 0
14
Taking false branch
914 metadata_len = cmd->metadata_len;
915 if (data_len + metadata_len) {
15
Assuming the condition is true
16
Taking true branch
916 payload = malloc(data_len + metadata_len);
17
Memory is allocated
917 if (!payload) {
18
Assuming 'payload' is non-null
19
Taking false branch
918 orig_close(ipc_fd);
919 errno(*__errno_location ()) = ENOMEM12;
920 return -1;
921 }
922 if (data_len
19.1
'data_len' is not equal to 0
)
20
Taking true branch
923 memcpy(payload, (void *)(uintptr_t)cmd->addr, data_len);
924 if (metadata_len
20.1
'metadata_len' is 0
)
21
Taking false branch
925 memcpy((uint8_t *)payload + data_len,
926 (void *)(uintptr_t)cmd->metadata, metadata_len);
927 }
928
929 struct ipc_request req = {
22
'?' condition is true
23
'?' condition is true
930 .type = IPC_TYPE_IOCTL,
931 .fd = instance, /* controller instance, not a real fd */
932 .data_len = data_len + metadata_len,
933 .request = request,
934 .opcode = cmd->opcode,
935 .nsid = cmd->nsid,
936 .cdw10 = cmd->cdw10,
937 .cdw11 = cmd->cdw11,
938 .cdw12 = cmd->cdw12,
939 .cdw13 = cmd->cdw13,
940 .cdw14 = cmd->cdw14,
941 .cdw15 = cmd->cdw15,
942 .req_len = cmd->data_len,
943 .flags = cmd->flags,
944 .cmd_flags = (admin
21.1
'admin' is true
? IPC_CMD_ADMIN : 0) |
945 (data_len
22.1
'data_len' is not equal to 0
? IPC_CMD_DATA : 0) |
946 (metadata_len
23.1
'metadata_len' is 0
? IPC_CMD_METADATA : 0),
24
'?' condition is false
947 .cdw2 = cmd->cdw2,
948 .cdw3 = cmd->cdw3,
949 .metadata_len = cmd->metadata_len,
950 .timeout_ms = cmd->timeout_ms,
951 };
952
953 resp_data = ipc_call(ipc_fd, &req, payload, &resp);
25
Potential leak of memory pointed to by 'payload'
954 mock_dbg("ioctl received IPC response status %d, sc_status %d, result %llu, data_len %u\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl received IPC response status %d, sc_status %d, result %llu, data_len %u\n"
, resp.status, resp.sc_status, (unsigned long long)resp.result
, resp.data_len); } while (0)
955 resp.status, resp.sc_status,do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl received IPC response status %d, sc_status %d, result %llu, data_len %u\n"
, resp.status, resp.sc_status, (unsigned long long)resp.result
, resp.data_len); } while (0)
956 (unsigned long long)resp.result, resp.data_len)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl received IPC response status %d, sc_status %d, result %llu, data_len %u\n"
, resp.status, resp.sc_status, (unsigned long long)resp.result
, resp.data_len); } while (0)
;
957
958 if (resp.status == -1) {
959 free(resp_data);
960 orig_close(ipc_fd);
961 errno(*__errno_location ()) = resp.errno_val;
962 return -1;
963 }
964
965 if (request == LIBNVME_IOCTL_ADMIN_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x41)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
||
966 request == LIBNVME_IOCTL_IO_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x43)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
) {
967 struct linux_passthru_cmd32 *cmd32 =
968 (struct linux_passthru_cmd32 *)cmd;
969
970 cmd32->result = resp.result;
971 } else {
972 cmd->result = resp.result;
973 }
974
975 /*
976 * like the kernel, which transfers no data without a buffer, and
977 * none back to the caller for a write
978 */
979 if (resp_data && cmd->addr && !write) {
980 if (resp.data_len > cmd->data_len) {
981 mock_dbg("ioctl response data_len %u exceeds caller buffer %u, truncating\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl response data_len %u exceeds caller buffer %u, truncating\n"
, resp.data_len, cmd->data_len); } while (0)
982 resp.data_len, cmd->data_len)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl response data_len %u exceeds caller buffer %u, truncating\n"
, resp.data_len, cmd->data_len); } while (0)
;
983 resp.data_len = cmd->data_len;
984 }
985 memcpy((void *)(uintptr_t)cmd->addr, resp_data, resp.data_len);
986 }
987 free(resp_data);
988 orig_close(ipc_fd);
989
990 return resp.sc_status;
991}
992
993/*
994 * Forward an ioctl() the shim doesn't decode itself. The argument bytes
995 * are sent as they are, in this process's byte order, and are copied
996 * back for an ioctl that returns data. The server answers an ioctl it
997 * doesn't handle with ENOTTY, like the /dev/null fallback used to.
998 */
999static int handle_raw_ioctl(int instance, unsigned long request, void *argp)
1000{
1001 uint32_t size = _IOC_SIZE(request)(((request) >> ((0 +8)+8)) & ((1 << 14)-1));
1002 struct ipc_response resp;
1003 void *resp_data;
1004 uint8_t dir = 0;
1005 int ipc_fd;
1006
1007 if (_IOC_DIR(request)(((request) >> (((0 +8)+8)+14)) & ((1 << 2)-1
))
& _IOC_WRITE1U)
1008 dir |= IPC_IOC_WRITE;
1009 if (_IOC_DIR(request)(((request) >> (((0 +8)+8)+14)) & ((1 << 2)-1
))
& _IOC_READ2U)
1010 dir |= IPC_IOC_READ;
1011 if (!dir || !argp)
1012 size = 0;
1013
1014 mock_dbg("raw ioctl intercepted on instance %d (request 0x%lx)\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "raw ioctl intercepted on instance %d (request 0x%lx)\n"
, instance, request); } while (0)
1015 instance, request)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "raw ioctl intercepted on instance %d (request 0x%lx)\n"
, instance, request); } while (0)
;
1016
1017 ipc_fd = connect_ipc_socket();
1018 if (ipc_fd < 0) {
1019 mock_dbg("raw ioctl could not connect to IPC socket\n")do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "raw ioctl could not connect to IPC socket\n"
); } while (0)
;
1020 return -2;
1021 }
1022
1023 struct ipc_request req = {
1024 .type = IPC_TYPE_RAW_IOCTL,
1025 .fd = instance, /* controller instance, not a real fd */
1026 .data_len = (dir & IPC_IOC_WRITE) ? size : 0,
1027 .request = request,
1028 .ioc_dir = dir,
1029 .ioc_type = _IOC_TYPE(request)(((request) >> (0 +8)) & ((1 << 8)-1)),
1030 .ioc_nr = _IOC_NR(request)(((request) >> 0) & ((1 << 8)-1)),
1031 .big_endian = __BYTE_ORDER1234 == __BIG_ENDIAN4321,
1032 .ioc_size = size,
1033 };
1034
1035 resp_data = ipc_call(ipc_fd, &req, argp, &resp);
1036 mock_dbg("raw ioctl received IPC response status %d, errno %d, sc_status %d, data_len %u\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "raw ioctl received IPC response status %d, errno %d, sc_status %d, data_len %u\n"
, resp.status, resp.errno_val, resp.sc_status, resp.data_len)
; } while (0)
1037 resp.status, resp.errno_val, resp.sc_status, resp.data_len)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "raw ioctl received IPC response status %d, errno %d, sc_status %d, data_len %u\n"
, resp.status, resp.errno_val, resp.sc_status, resp.data_len)
; } while (0)
;
1038 orig_close(ipc_fd);
1039
1040 if (resp.status == -1) {
1041 free(resp_data);
1042 errno(*__errno_location ()) = resp.errno_val;
1043 return -1;
1044 }
1045
1046 if (resp_data && dir & IPC_IOC_READ)
1047 memcpy(argp, resp_data, resp.data_len < size ?
1048 resp.data_len : size);
1049 free(resp_data);
1050
1051 return resp.sc_status;
1052}
1053
1054/*
1055 * Ask the IPC server for the h/w queues of the interface in an
1056 * ETHTOOL_GCHANNELS request. Any other ethtool command goes to the real
1057 * ioctl().
1058 */
1059static int handle_ethtool_ioctl(struct ifreq *ifr)
1060{
1061 struct ethtool_channels *chan = (void *)ifr->ifr_dataifr_ifru.ifru_data;
1062 struct ipc_response resp;
1063 uint32_t *counts;
1064 int ipc_fd;
1065
1066 if (!chan || chan->cmd != ETHTOOL_GCHANNELS0x0000003c)
1067 return -2;
1068
1069 ipc_fd = connect_ipc_socket();
1070 if (ipc_fd < 0)
1071 return -2;
1072
1073 mock_dbg("ETHTOOL_GCHANNELS intercepted on %s\n", ifr->ifr_name)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ETHTOOL_GCHANNELS intercepted on %s\n"
, ifr->ifr_ifrn.ifrn_name); } while (0)
;
1074
1075 struct ipc_request req = {
1076 .type = IPC_TYPE_NIC_QUEUES,
1077 .data_len = strnlen(ifr->ifr_nameifr_ifrn.ifrn_name, sizeof(ifr->ifr_nameifr_ifrn.ifrn_name)),
1078 };
1079
1080 counts = ipc_call(ipc_fd, &req, ifr->ifr_nameifr_ifrn.ifrn_name, &resp);
1081 orig_close(ipc_fd);
1082
1083 if (resp.status == -1) {
1084 free(counts);
1085 errno(*__errno_location ()) = resp.errno_val;
1086 return -1;
1087 }
1088
1089 if (!counts || resp.data_len < 3 * sizeof(*counts)) {
1090 free(counts);
1091 errno(*__errno_location ()) = EIO5;
1092 return -1;
1093 }
1094
1095 chan->combined_count = le32toh(counts[0])__uint32_identity (counts[0]);
1096 chan->tx_count = le32toh(counts[1])__uint32_identity (counts[1]);
1097 chan->rx_count = le32toh(counts[2])__uint32_identity (counts[2]);
1098 free(counts);
1099
1100 return 0;
1101}
1102
1103static int mock_ioctl(int fd, unsigned long request, void *argp, orig_ioctl_t fallback)
1104{
1105 int instance = mock_ctrl_instance(fd);
1106 int ret;
1107
1108 if (request == SIOCETHTOOL0x8946 && argp) {
2
Assuming 'request' is not equal to SIOCETHTOOL
1109 ret = handle_ethtool_ioctl(argp);
1110 if (ret != -2)
1111 return ret;
1112 }
1113
1114 if (instance < 0)
3
Assuming 'instance' is >= 0
1115 return fallback(fd, request, argp);
1116
1117 if (request == LIBNVME_IOCTL_ADMIN_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x41)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
||
4
Assuming the condition is true
1118 request == LIBNVME_IOCTL_ADMIN64_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x47)) << 0) | ((((sizeof(struct linux_passthru_cmd64
)))) << ((0 +8)+8)))
||
1119 request == LIBNVME_IOCTL_IO_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x43)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
||
1120 request == LIBNVME_IOCTL_IO64_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x48)) << 0) | ((((sizeof(struct linux_passthru_cmd64
)))) << ((0 +8)+8)))
)
1121 ret = handle_passthru_ioctl(instance, request, argp);
5
Calling 'handle_passthru_ioctl'
1122 else
1123 ret = handle_raw_ioctl(instance, request, argp);
1124 if (ret != -2)
1125 return ret;
1126
1127 return fallback(fd, request, argp);
1128}
1129
1130int ioctl(int fd, unsigned long request, ...)
1131{
1132 va_list args;
1133 void *argp;
1134
1135 init_orig_functions();
1136
1137 va_start(args, request)__builtin_va_start(args, request);
1138 argp = va_arg(args, void *)__builtin_va_arg(args, void *);
1139 va_end(args)__builtin_va_end(args);
1140
1141 return mock_ioctl(fd, request, argp, orig_ioctl);
1142}
1143
1144int __ioctl(int fd, unsigned long request, ...)
1145{
1146 va_list args;
1147 void *argp;
1148
1149 va_start(args, request)__builtin_va_start(args, request);
1150 argp = va_arg(args, void *)__builtin_va_arg(args, void *);
1151 va_end(args)__builtin_va_end(args);
1152
1153 return ioctl(fd, request, argp);
1154}
1155
1156int __ioctl_time64(int fd, unsigned long request, ...)
1157{
1158 va_list args;
1159 void *argp;
1160
1161 init_orig_functions();
1162
1163 va_start(args, request)__builtin_va_start(args, request);
1164 argp = va_arg(args, void *)__builtin_va_arg(args, void *);
1165 va_end(args)__builtin_va_end(args);
1166
1167 return mock_ioctl(fd, request, argp, orig_ioctl_time64);
1
Calling 'mock_ioctl'
1168}
1169
1170int close(int fd)
1171{
1172 init_orig_functions();
1173
1174 if (fd == mock_fabrics_fd)
1175 mock_fabrics_fd = -1;
1176 if (fd == mock_fabrics_manager_fd)
1177 mock_fabrics_manager_fd = -1;
1178 if (fd == mock_netlink_fd) {
1179 mock_netlink_fd = -1;
1180 mock_route_reply.pending = false0;
1181 }
1182
1183 for (int i = 0; i < num_mock_ctrls; i++) {
1184 if (mock_ctrl_fds[i].fd != fd)
1185 continue;
1186 for (int j = i; j < num_mock_ctrls - 1; j++)
1187 mock_ctrl_fds[j] = mock_ctrl_fds[j + 1];
1188 num_mock_ctrls--;
1189 break;
1190 }
1191
1192 return orig_close(fd);
1193}
1194
1195int fstat(int fd, struct stat *buf)
1196{
1197 bool_Bool is_block;
1198
1199 init_orig_functions();
1200
1201 if (mock_ctrl_is_block(fd, &is_block)) {
1202 memset(buf, 0, sizeof(*buf));
1203 buf->st_mode = (is_block ? S_IFBLK0060000 : S_IFCHR0020000) | 0600;
1204 return 0;
1205 }
1206
1207 return orig_fstat(fd, buf);
1208}
1209
1210int fstat64(int fd, struct stat64 *buf)
1211{
1212 bool_Bool is_block;
1213
1214 init_orig_functions();
1215
1216 if (mock_ctrl_is_block(fd, &is_block)) {
1217 memset(buf, 0, sizeof(*buf));
1218 buf->st_mode = (is_block ? S_IFBLK0060000 : S_IFCHR0020000) | 0600;
1219 return 0;
1220 }
1221
1222 return orig_fstat64(fd, buf);
1223}
1224
1225int __fstat64_time64(int fd, void *buf)
1226{
1227 bool_Bool is_block;
1228 int ret;
1229
1230 init_orig_functions();
1231
1232 /*
1233 * buf's real layout is unknown here, so unlike fstat()/fstat64() this
1234 * can't memset() the whole thing and fabricate it from scratch. Let the
1235 * real call fill in a correctly-sized, correctly-shaped struct, then
1236 * only patch st_mode -- its offset is unaffected by the time64
1237 * transition.
1238 */
1239 ret = orig_fstat64_time64(fd, buf);
1240 if (ret == 0 && mock_ctrl_is_block(fd, &is_block))
1241 ((struct stat *)buf)->st_mode = (is_block ? S_IFBLK0060000 : S_IFCHR0020000) | 0600;
1242
1243 return ret;
1244}
1245
1246/*
1247 * Ask the IPC server for the answer to a password prompt, instead of
1248 * reading the terminal. Without an IPC server, fall back to the real
1249 * getpass().
1250 */
1251char *getpass(const char *prompt)
1252{
1253 static char pass[128];
1254 struct ipc_response resp;
1255 char *resp_data;
1256 size_t len;
1257 int ipc_fd;
1258
1259 init_orig_functions();
1260
1261 ipc_fd = connect_ipc_socket();
1262 if (ipc_fd < 0)
1263 return orig_getpass(prompt);
1264
1265 struct ipc_request req = {
1266 .type = IPC_TYPE_GETPASS,
1267 .data_len = strlen(prompt),
1268 };
1269
1270 resp_data = ipc_call(ipc_fd, &req, prompt, &resp);
1271 orig_close(ipc_fd);
1272
1273 if (resp.status == -1) {
1274 free(resp_data);
1275 errno(*__errno_location ()) = resp.errno_val;
1276 return NULL((void*)0);
1277 }
1278
1279 len = resp.data_len < sizeof(pass) - 1 ?
1280 resp.data_len : sizeof(pass) - 1;
1281 if (resp_data)
1282 memcpy(pass, resp_data, len);
1283 pass[len] = '\0';
1284 free(resp_data);
1285
1286 return pass;
1287}
1288
1289/*
1290 * Fake the NETLINK_ROUTE socket the egress route lookup uses. The fd is
1291 * a real (unconnected) socket, so close() and friends still work, but
1292 * sendmsg()/recvmsg() on it never reach the kernel.
1293 */
1294int socket(int domain, int type, int protocol)
1295{
1296 int fd;
1297
1298 init_orig_functions();
1299
1300 if (domain != AF_NETLINK16 || protocol != NETLINK_ROUTE0 ||
1301 !getenv("MOCK_IPC_SOCK"))
1302 return orig_socket(domain, type, protocol);
1303
1304 fd = orig_socket(AF_UNIX1, SOCK_DGRAMSOCK_DGRAM, 0);
1305 if (fd < 0)
1306 return fd;
1307
1308 mock_netlink_fd = fd;
1309 mock_route_reply.pending = false0;
1310 mock_dbg("intercepted NETLINK_ROUTE socket (mapped to fd %d)\n", fd)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "intercepted NETLINK_ROUTE socket (mapped to fd %d)\n"
, fd); } while (0)
;
1311
1312 return fd;
1313}
1314
1315static void rtattr_addr_to_str(struct rtattr *rta, unsigned char family,
1316 char *buf, size_t len)
1317{
1318 size_t alen = family == AF_INET2 ? sizeof(struct in_addr) :
1319 sizeof(struct in6_addr);
1320
1321 if (RTA_PAYLOAD(rta)((int)((rta)->rta_len) - (( ((sizeof(struct rtattr))+4U -1
) & ~(4U -1) ) + (0)))
< alen ||
1322 !inet_ntop(family, RTA_DATA(rta)((void*)(((char*)(rta)) + (( ((sizeof(struct rtattr))+4U -1) &
~(4U -1) ) + (0))))
, buf, len))
1323 buf[0] = '\0';
1324}
1325
1326/*
1327 * Decode an RTM_GETROUTE request and ask the IPC server for the egress
1328 * interface. Its answer is returned by the next recvmsg().
1329 */
1330static ssize_t mock_route_sendmsg(const struct msghdr *msg)
1331{
1332 char daddr[INET6_ADDRSTRLEN46] = "", saddr[INET6_ADDRSTRLEN46] = "";
1333 char payload[2 * INET6_ADDRSTRLEN46];
1334 struct ipc_response resp;
1335 struct nlmsghdr *nlh;
1336 struct rtmsg *rtm;
1337 struct rtattr *rta;
1338 char *ifname;
1339 int attrlen, ipc_fd;
1340 size_t len;
1341
1342 if (!msg->msg_iovlen ||
1343 msg->msg_iov[0].iov_len < NLMSG_LENGTH(sizeof(*rtm))((sizeof(*rtm)) + ((int) ( ((sizeof(struct nlmsghdr))+4U -1) &
~(4U -1) )))
) {
1344 errno(*__errno_location ()) = EINVAL22;
1345 return -1;
1346 }
1347
1348 nlh = msg->msg_iov[0].iov_base;
1349 if (nlh->nlmsg_type != RTM_GETROUTERTM_GETROUTE ||
1350 nlh->nlmsg_len > msg->msg_iov[0].iov_len) {
1351 errno(*__errno_location ()) = EOPNOTSUPP95;
1352 return -1;
1353 }
1354
1355 rtm = NLMSG_DATA(nlh)((void *)(((char *)nlh) + ((int) ( ((sizeof(struct nlmsghdr))
+4U -1) & ~(4U -1) ))))
;
1356 rta = RTM_RTA(rtm)((struct rtattr*)(((char*)(rtm)) + ( ((sizeof(struct rtmsg))+
4U -1) & ~(4U -1) )))
;
1357 attrlen = RTM_PAYLOAD(nlh)((nlh)->nlmsg_len - ( (((((sizeof(struct rtmsg))) + ((int)
( ((sizeof(struct nlmsghdr))+4U -1) & ~(4U -1) ))))+4U -
1) & ~(4U -1) ))
;
1358 for (; RTA_OK(rta, attrlen)((attrlen) >= (int)sizeof(struct rtattr) && (rta)->
rta_len >= sizeof(struct rtattr) && (rta)->rta_len
<= (attrlen))
; rta = RTA_NEXT(rta, attrlen)((attrlen) -= ( (((rta)->rta_len)+4U -1) & ~(4U -1) ),
(struct rtattr*)(((char*)(rta)) + ( (((rta)->rta_len)+4U -
1) & ~(4U -1) )))
) {
1359 if (rta->rta_type == RTA_DST)
1360 rtattr_addr_to_str(rta, rtm->rtm_family, daddr,
1361 sizeof(daddr));
1362 else if (rta->rta_type == RTA_SRC)
1363 rtattr_addr_to_str(rta, rtm->rtm_family, saddr,
1364 sizeof(saddr));
1365 }
1366
1367 mock_dbg("route lookup intercepted, daddr '%s', saddr '%s'\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "route lookup intercepted, daddr '%s', saddr '%s'\n"
, daddr, saddr); } while (0)
1368 daddr, saddr)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "route lookup intercepted, daddr '%s', saddr '%s'\n"
, daddr, saddr); } while (0)
;
1369
1370 mock_route_reply.req = *nlh;
1371 mock_route_reply.family = rtm->rtm_family;
1372 mock_route_reply.error = 0;
1373 mock_route_reply.pending = true1;
1374
1375 ipc_fd = connect_ipc_socket();
1376 if (ipc_fd < 0) {
1377 mock_route_reply.error = EIO5;
1378 return nlh->nlmsg_len;
1379 }
1380
1381 len = strlen(daddr) + 1;
1382 memcpy(payload, daddr, len);
1383 memcpy(payload + len, saddr, strlen(saddr));
1384 len += strlen(saddr);
1385
1386 struct ipc_request req = {
1387 .type = IPC_TYPE_ROUTE,
1388 .data_len = len,
1389 };
1390
1391 ifname = ipc_call(ipc_fd, &req, payload, &resp);
1392 orig_close(ipc_fd);
1393
1394 if (resp.status == -1) {
1395 mock_route_reply.error = resp.errno_val;
1396 } else if (!ifname || !resp.data_len ||
1397 resp.data_len >= sizeof(mock_ifname)) {
1398 mock_route_reply.error = EIO5;
1399 } else {
1400 memcpy(mock_ifname, ifname, resp.data_len);
1401 mock_ifname[resp.data_len] = '\0';
1402 }
1403 free(ifname);
1404
1405 return nlh->nlmsg_len;
1406}
1407
1408/*
1409 * Build the kernel's answer to the pending RTM_GETROUTE request: an
1410 * RTM_NEWROUTE with the egress interface in RTA_OIF, or an NLMSG_ERROR.
1411 */
1412static ssize_t mock_route_recvmsg(struct msghdr *msg)
1413{
1414 struct nlmsghdr *nlh;
1415 size_t len;
1416
1417 if (!mock_route_reply.pending) {
1418 errno(*__errno_location ()) = EAGAIN11;
1419 return -1;
1420 }
1421 mock_route_reply.pending = false0;
1422
1423 if (mock_route_reply.error)
1424 len = NLMSG_LENGTH(sizeof(struct nlmsgerr))((sizeof(struct nlmsgerr)) + ((int) ( ((sizeof(struct nlmsghdr
))+4U -1) & ~(4U -1) )))
;
1425 else
1426 len = NLMSG_LENGTH(sizeof(struct rtmsg))((sizeof(struct rtmsg)) + ((int) ( ((sizeof(struct nlmsghdr))
+4U -1) & ~(4U -1) )))
+
1427 RTA_SPACE(sizeof(int))( (((( ((sizeof(struct rtattr))+4U -1) & ~(4U -1) ) + (sizeof
(int))))+4U -1) & ~(4U -1) )
;
1428
1429 if (!msg->msg_iovlen || msg->msg_iov[0].iov_len < len) {
1430 errno(*__errno_location ()) = ENOBUFS105;
1431 return -1;
1432 }
1433
1434 nlh = msg->msg_iov[0].iov_base;
1435 memset(nlh, 0, len);
1436 nlh->nlmsg_len = len;
1437 nlh->nlmsg_seq = mock_route_reply.req.nlmsg_seq;
1438
1439 if (mock_route_reply.error) {
1440 struct nlmsgerr *err = NLMSG_DATA(nlh)((void *)(((char *)nlh) + ((int) ( ((sizeof(struct nlmsghdr))
+4U -1) & ~(4U -1) ))))
;
1441
1442 nlh->nlmsg_type = NLMSG_ERROR0x2;
1443 err->error = -mock_route_reply.error;
1444 err->msg = mock_route_reply.req;
1445 } else {
1446 struct rtmsg *rtm = NLMSG_DATA(nlh)((void *)(((char *)nlh) + ((int) ( ((sizeof(struct nlmsghdr))
+4U -1) & ~(4U -1) ))))
;
1447 struct rtattr *rta = RTM_RTA(rtm)((struct rtattr*)(((char*)(rtm)) + ( ((sizeof(struct rtmsg))+
4U -1) & ~(4U -1) )))
;
1448 int ifindex = MOCK_IFINDEX0x4d4f434b;
1449
1450 nlh->nlmsg_type = RTM_NEWROUTERTM_NEWROUTE;
1451 rtm->rtm_family = mock_route_reply.family;
1452 rta->rta_type = RTA_OIF;
1453 rta->rta_len = RTA_LENGTH(sizeof(ifindex))(( ((sizeof(struct rtattr))+4U -1) & ~(4U -1) ) + (sizeof
(ifindex)))
;
1454 memcpy(RTA_DATA(rta)((void*)(((char*)(rta)) + (( ((sizeof(struct rtattr))+4U -1) &
~(4U -1) ) + (0))))
, &ifindex, sizeof(ifindex));
1455 }
1456
1457 return len;
1458}
1459
1460ssize_t sendmsg(int fd, const struct msghdr *msg, int flags)
1461{
1462 init_orig_functions();
1463
1464 if (fd == mock_netlink_fd)
1465 return mock_route_sendmsg(msg);
1466
1467 return orig_sendmsg(fd, msg, flags);
1468}
1469
1470ssize_t __sendmsg64(int fd, const struct msghdr *msg, int flags)
1471{
1472 init_orig_functions();
1473
1474 if (fd == mock_netlink_fd)
1475 return mock_route_sendmsg(msg);
1476
1477 return orig_sendmsg64(fd, msg, flags);
1478}
1479
1480ssize_t recvmsg(int fd, struct msghdr *msg, int flags)
1481{
1482 init_orig_functions();
1483
1484 if (fd == mock_netlink_fd)
1485 return mock_route_recvmsg(msg);
1486
1487 return orig_recvmsg(fd, msg, flags);
1488}
1489
1490ssize_t __recvmsg64(int fd, struct msghdr *msg, int flags)
1491{
1492 init_orig_functions();
1493
1494 if (fd == mock_netlink_fd)
1495 return mock_route_recvmsg(msg);
1496
1497 return orig_recvmsg64(fd, msg, flags);
1498}
1499
1500char *if_indextoname(unsigned int ifindex, char ifname[IF_NAMESIZE16])
1501{
1502 init_orig_functions();
1503
1504 if (ifindex != MOCK_IFINDEX0x4d4f434b || !mock_ifname[0])
1505 return orig_if_indextoname(ifindex, ifname);
1506
1507 memcpy(ifname, mock_ifname, sizeof(mock_ifname));
1508
1509 return ifname;
1510}
1511
1512/*
1513 * Ask the IPC server for the number of online CPUs. Without an IPC
1514 * server, or if it doesn't set one, fall back to the real get_nprocs().
1515 */
1516int get_nprocs(void)
1517{
1518 struct ipc_response resp;
1519 void *resp_data;
1520 int ipc_fd;
1521
1522 init_orig_functions();
1523
1524 ipc_fd = connect_ipc_socket();
1525 if (ipc_fd < 0)
1526 return orig_get_nprocs();
1527
1528 struct ipc_request req = {
1529 .type = IPC_TYPE_NPROCS,
1530 };
1531
1532 resp_data = ipc_call(ipc_fd, &req, NULL((void*)0), &resp);
1533 orig_close(ipc_fd);
1534 free(resp_data);
1535
1536 if (resp.status == -1)
1537 return orig_get_nprocs();
1538
1539 return resp.result;
1540}