Bug Summary

File:.build-ci/../tests/cli/libmock_nvme.c
Warning:line 950, 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-10-24-125112-639-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 uint32_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 = le32toh(r->result)__uint32_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#ifdef MOCK_FUZZ
588/*
589 * The kernel rejects a transfer larger than the controller takes. And a
590 * fuzzed log length must not have the mock serve, and nvme-cli print,
591 * gigabytes: the data of all commands of a run is limited too. A
592 * megabyte of log entries already prints as tens of megabytes of text,
593 * which takes seconds with the sanitizers. FUZZ_DATA_LIMIT sets another
594 * limit in bytes: most of the data structures fit a page, and with less
595 * data an input runs faster.
596 */
597#define FUZZ_MAX_TRANSFER (2 * 1024 * 1024)
598#define FUZZ_MAX_TOTAL (1 * 1024 * 1024)
599
600static const uint8_t *fuzz_data;
601static size_t fuzz_size;
602static size_t fuzz_total;
603static size_t fuzz_limit;
604
605static size_t fuzz_data_limit(void)
606{
607 unsigned long long limit;
608 const char *env;
609 char *end;
610
611 if (fuzz_limit)
612 return fuzz_limit;
613 fuzz_limit = FUZZ_MAX_TOTAL;
614 env = getenv("FUZZ_DATA_LIMIT");
615 if (!env)
616 return fuzz_limit;
617 errno(*__errno_location ()) = 0;
618 limit = strtoull(env, &end, 10);
619 if (errno(*__errno_location ()) || end == env || *end || !limit ||
620 (size_t)limit != limit) {
621 fprintf(stderrstderr, "FUZZ_DATA_LIMIT=%s: not a size in bytes\n",
622 env);
623 exit(EXIT_FAILURE1);
624 }
625 fuzz_limit = limit;
626 return fuzz_limit;
627}
628
629/* The bytes the following commands get their answers from */
630void mock_fuzz_input(const uint8_t *data, size_t size)
631{
632 fuzz_data = data;
633 fuzz_size = size;
634 fuzz_total = 0;
635}
636
637/* The next bytes of the input, zeros once it is used up */
638static void fuzz_read(void *buf, size_t len)
639{
640 size_t n = len < fuzz_size ? len : fuzz_size;
641
642 memcpy(buf, fuzz_data, n);
643 memset((uint8_t *)buf + n, 0, len - n);
644 fuzz_data += n;
645 fuzz_size -= n;
646}
647
648/*
649 * Answer a command from the fuzz input: a control byte, the result
650 * dword, and then the data the command reads. Most control bytes mean
651 * success, the rest an NVMe status (from the next two bytes) or a
652 * failed ioctl(). Everything that is no device command fails, so the
653 * real function takes over where there is one.
654 */
655static void *fuzz_ipc_call(struct ipc_request *req,
656 struct ipc_response *resp)
657{
658 uint8_t ctl;
659 uint16_t sc;
660 void *buf;
661
662 memset(resp, 0, sizeof(*resp));
663
664 switch (req->type) {
665 case IPC_TYPE_IOCTL:
666 resp->data_len = req->req_len;
667 break;
668 case IPC_TYPE_RAW_IOCTL:
669 if (req->ioc_dir & IPC_IOC_READ)
670 resp->data_len = req->ioc_size;
671 break;
672 default:
673 resp->status = -1;
674 resp->errno_val = EOPNOTSUPP95;
675 return NULL((void*)0);
676 }
677
678 if (resp->data_len > FUZZ_MAX_TRANSFER) {
679 resp->status = -1;
680 resp->errno_val = EINVAL22;
681 return NULL((void*)0);
682 }
683 if (resp->data_len > fuzz_data_limit() - fuzz_total) {
684 resp->status = -1;
685 resp->errno_val = EIO5;
686 return NULL((void*)0);
687 }
688 fuzz_total += resp->data_len;
689
690 fuzz_read(&ctl, sizeof(ctl));
691 fuzz_read(&resp->result, sizeof(resp->result));
692 resp->result = le32toh(resp->result)__uint32_identity (resp->result);
693 if (ctl >= 0xf8) {
694 resp->status = -1;
695 resp->errno_val = EIO5;
696 return NULL((void*)0);
697 }
698 if (ctl >= 0xe0) {
699 fuzz_read(&sc, sizeof(sc));
700 resp->sc_status = le16toh(sc)__uint16_identity (sc) & 0x7fff;
701 return NULL((void*)0);
702 }
703
704 if (!resp->data_len)
705 return NULL((void*)0);
706 buf = malloc(resp->data_len);
707 if (!buf) {
708 resp->status = -1;
709 resp->errno_val = ENOMEM12;
710 return NULL((void*)0);
711 }
712 fuzz_read(buf, resp->data_len);
713
714 return buf;
715}
716#else
717static bool_Bool read_full(int fd, void *buf, size_t len)
718{
719 size_t total_read = 0;
720
721 while (total_read < len) {
722 ssize_t n = orig_read(fd, (char *)buf + total_read,
723 len - total_read);
724 if (n < 0 && errno(*__errno_location ()) == EINTR4)
725 continue;
726 if (n <= 0)
727 return false0;
728 total_read += n;
729 }
730
731 return true1;
732}
733
734static bool_Bool write_full(int fd, const void *buf, size_t len)
735{
736 size_t total_written = 0;
737
738 while (total_written < len) {
739 ssize_t n = orig_write(fd, (const char *)buf + total_written,
740 len - total_written);
741 if (n < 0 && errno(*__errno_location ()) == EINTR4)
742 continue;
743 if (n <= 0)
744 return false0;
745 total_written += n;
746 }
747
748 return true1;
749}
750
751static void *read_ipc_response(int ipc_fd, struct ipc_response *resp)
752{
753 char *payload;
754
755 memset(resp, 0, sizeof(*resp));
756 if (!read_full(ipc_fd, resp, sizeof(*resp))) {
757 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)
;
758 resp->status = -1;
759 resp->errno_val = EIO5;
760 return NULL((void*)0);
761 }
762 ipc_response_decode(resp);
763
764 if (!resp->data_len)
765 return NULL((void*)0);
766
767 payload = malloc(resp->data_len);
768 if (!payload)
769 return NULL((void*)0);
770
771 if (!read_full(ipc_fd, payload, resp->data_len)) {
772 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)
;
773 free(payload);
774 resp->status = -1;
775 resp->errno_val = EIO5;
776 return NULL((void*)0);
777 }
778
779 return payload;
780}
781
782#endif
783
784/*
785 * Send a request and its data_len bytes of data, and read the response.
786 * A failed send is reported like a failed read.
787 */
788static void *ipc_call(int ipc_fd, struct ipc_request *req, const void *data,
789 struct ipc_response *resp)
790{
791#ifdef MOCK_FUZZ
792 (void)ipc_fd;
793 (void)data;
794 return fuzz_ipc_call(req, resp);
795#else
796 size_t len = req->data_len;
797
798 ipc_request_encode(req);
799 if (!write_full(ipc_fd, req, sizeof(*req)) ||
800 (len && !write_full(ipc_fd, data, len))) {
801 mock_dbg("failed to send IPC request\n")do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "failed to send IPC request\n"
); } while (0)
;
802 memset(resp, 0, sizeof(*resp));
803 resp->status = -1;
804 resp->errno_val = EIO5;
805 return NULL((void*)0);
806 }
807
808 return read_ipc_response(ipc_fd, resp);
809#endif
810}
811
812ssize_t write(int fd, const void *buf, size_t count)
813{
814 struct ipc_response resp;
815 char *resp_data;
816 int ipc_fd;
817
818 init_orig_functions();
819
820 if (fd != mock_fabrics_fd)
821 return orig_write(fd, buf, count);
822
823 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)
;
824
825 ipc_fd = connect_ipc_socket();
826 if (ipc_fd < 0) {
827 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)
;
828 return orig_write(fd, buf, count);
829 }
830
831 struct ipc_request req = {
832 .type = IPC_TYPE_WRITE,
833 .fd = fd,
834 .data_len = count,
835 };
836
837 resp_data = ipc_call(ipc_fd, &req, buf, &resp);
838 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)
839 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)
;
840
841 if (resp.status == -1) {
842 free(resp_data);
843 orig_close(ipc_fd);
844 errno(*__errno_location ()) = resp.errno_val;
845 return -1;
846 }
847
848 if (resp_data && mock_fabrics_manager_fd >= 0)
849 orig_write(mock_fabrics_manager_fd, resp_data, resp.data_len);
850 free(resp_data);
851 orig_close(ipc_fd);
852
853 return count;
854}
855
856static int mock_ctrl_instance(int fd)
857{
858 for (int i = 0; i < num_mock_ctrls; i++)
859 if (mock_ctrl_fds[i].fd == fd)
860 return mock_ctrl_fds[i].instance;
861
862 return -1;
863}
864
865static bool_Bool mock_ctrl_is_block(int fd, bool_Bool *is_block)
866{
867 for (int i = 0; i < num_mock_ctrls; i++) {
868 if (mock_ctrl_fds[i].fd == fd) {
869 *is_block = mock_ctrl_fds[i].is_block;
870 return true1;
871 }
872 }
873
874 return false0;
875}
876
877/*
878 * Bits 1:0 of an opcode give the direction of its data transfer. Like
879 * the kernel, treat a bidirectional command as a write.
880 */
881static bool_Bool opcode_is_write(uint8_t opcode)
882{
883 return opcode & 1;
884}
885
886static int handle_passthru_ioctl(int instance, unsigned long request,
887 struct linux_passthru_cmd64 *cmd)
888{
889 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)))
||
890 request == LIBNVME_IOCTL_ADMIN64_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x47)) << 0) | ((((sizeof(struct linux_passthru_cmd64
)))) << ((0 +8)+8)))
;
891 bool_Bool write = opcode_is_write(cmd->opcode);
892 uint32_t data_len = 0, metadata_len = 0;
893 __cleanup_free__attribute__((cleanup(shr_freep))) void *payload = NULL((void*)0);
894 struct ipc_response resp;
895 void *resp_data;
896 int ipc_fd;
897
898 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
899 instance, request)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl intercepted on instance %d (request 0x%lx)\n"
, instance, request); } while (0)
;
900
901
902 ipc_fd = connect_ipc_socket();
903 if (ipc_fd < 0) {
9
Assuming 'ipc_fd' is >= 0
904 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)
;
905 return -2;
906 }
907
908 if (write && cmd->addr)
10
Assuming 'write' is true
11
Assuming field 'addr' is not equal to 0
12
Taking true branch
909 data_len = cmd->data_len;
910 if (write
12.1
'write' is true
&& cmd->metadata)
13
Assuming field 'metadata' is 0
14
Taking false branch
911 metadata_len = cmd->metadata_len;
912 if (data_len + metadata_len) {
15
Assuming the condition is true
16
Taking true branch
913 payload = malloc(data_len + metadata_len);
17
Memory is allocated
914 if (!payload) {
18
Assuming 'payload' is non-null
19
Taking false branch
915 orig_close(ipc_fd);
916 errno(*__errno_location ()) = ENOMEM12;
917 return -1;
918 }
919 if (data_len
19.1
'data_len' is not equal to 0
)
20
Taking true branch
920 memcpy(payload, (void *)(uintptr_t)cmd->addr, data_len);
921 if (metadata_len
20.1
'metadata_len' is 0
)
21
Taking false branch
922 memcpy((uint8_t *)payload + data_len,
923 (void *)(uintptr_t)cmd->metadata, metadata_len);
924 }
925
926 struct ipc_request req = {
22
'?' condition is true
23
'?' condition is true
927 .type = IPC_TYPE_IOCTL,
928 .fd = instance, /* controller instance, not a real fd */
929 .data_len = data_len + metadata_len,
930 .request = request,
931 .opcode = cmd->opcode,
932 .nsid = cmd->nsid,
933 .cdw10 = cmd->cdw10,
934 .cdw11 = cmd->cdw11,
935 .cdw12 = cmd->cdw12,
936 .cdw13 = cmd->cdw13,
937 .cdw14 = cmd->cdw14,
938 .cdw15 = cmd->cdw15,
939 .req_len = cmd->data_len,
940 .flags = cmd->flags,
941 .cmd_flags = (admin
21.1
'admin' is true
? IPC_CMD_ADMIN : 0) |
942 (data_len
22.1
'data_len' is not equal to 0
? IPC_CMD_DATA : 0) |
943 (metadata_len
23.1
'metadata_len' is 0
? IPC_CMD_METADATA : 0),
24
'?' condition is false
944 .cdw2 = cmd->cdw2,
945 .cdw3 = cmd->cdw3,
946 .metadata_len = cmd->metadata_len,
947 .timeout_ms = cmd->timeout_ms,
948 };
949
950 resp_data = ipc_call(ipc_fd, &req, payload, &resp);
25
Potential leak of memory pointed to by 'payload'
951 mock_dbg("ioctl received IPC response status %d, sc_status %d, result %u, data_len %u\n",do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl received IPC response status %d, sc_status %d, result %u, data_len %u\n"
, resp.status, resp.sc_status, resp.result, resp.data_len); }
while (0)
952 resp.status, resp.sc_status, resp.result, resp.data_len)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "ioctl received IPC response status %d, sc_status %d, result %u, data_len %u\n"
, resp.status, resp.sc_status, resp.result, resp.data_len); }
while (0)
;
953
954 if (resp.status == -1) {
955 free(resp_data);
956 orig_close(ipc_fd);
957 errno(*__errno_location ()) = resp.errno_val;
958 return -1;
959 }
960
961 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)))
||
962 request == LIBNVME_IOCTL_IO_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x43)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
) {
963 struct linux_passthru_cmd32 *cmd32 =
964 (struct linux_passthru_cmd32 *)cmd;
965
966 cmd32->result = resp.result;
967 } else {
968 cmd->result = resp.result;
969 }
970
971 /*
972 * like the kernel, which transfers no data without a buffer, and
973 * none back to the caller for a write
974 */
975 if (resp_data && cmd->addr && !write) {
976 if (resp.data_len > cmd->data_len) {
977 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)
978 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)
;
979 resp.data_len = cmd->data_len;
980 }
981 memcpy((void *)(uintptr_t)cmd->addr, resp_data, resp.data_len);
982 }
983 free(resp_data);
984 orig_close(ipc_fd);
985
986 return resp.sc_status;
987}
988
989/*
990 * Forward an ioctl() the shim doesn't decode itself. The argument bytes
991 * are sent as they are, in this process's byte order, and are copied
992 * back for an ioctl that returns data. The server answers an ioctl it
993 * doesn't handle with ENOTTY, like the /dev/null fallback used to.
994 */
995static int handle_raw_ioctl(int instance, unsigned long request, void *argp)
996{
997 uint32_t size = _IOC_SIZE(request)(((request) >> ((0 +8)+8)) & ((1 << 14)-1));
998 struct ipc_response resp;
999 void *resp_data;
1000 uint8_t dir = 0;
1001 int ipc_fd;
1002
1003 if (_IOC_DIR(request)(((request) >> (((0 +8)+8)+14)) & ((1 << 2)-1
))
& _IOC_WRITE1U)
1004 dir |= IPC_IOC_WRITE;
1005 if (_IOC_DIR(request)(((request) >> (((0 +8)+8)+14)) & ((1 << 2)-1
))
& _IOC_READ2U)
1006 dir |= IPC_IOC_READ;
1007 if (!dir || !argp)
1008 size = 0;
1009
1010 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)
1011 instance, request)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "raw ioctl intercepted on instance %d (request 0x%lx)\n"
, instance, request); } while (0)
;
1012
1013 ipc_fd = connect_ipc_socket();
1014 if (ipc_fd < 0) {
1015 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)
;
1016 return -2;
1017 }
1018
1019 struct ipc_request req = {
1020 .type = IPC_TYPE_RAW_IOCTL,
1021 .fd = instance, /* controller instance, not a real fd */
1022 .data_len = (dir & IPC_IOC_WRITE) ? size : 0,
1023 .request = request,
1024 .ioc_dir = dir,
1025 .ioc_type = _IOC_TYPE(request)(((request) >> (0 +8)) & ((1 << 8)-1)),
1026 .ioc_nr = _IOC_NR(request)(((request) >> 0) & ((1 << 8)-1)),
1027 .big_endian = __BYTE_ORDER1234 == __BIG_ENDIAN4321,
1028 .ioc_size = size,
1029 };
1030
1031 resp_data = ipc_call(ipc_fd, &req, argp, &resp);
1032 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)
1033 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)
;
1034 orig_close(ipc_fd);
1035
1036 if (resp.status == -1) {
1037 free(resp_data);
1038 errno(*__errno_location ()) = resp.errno_val;
1039 return -1;
1040 }
1041
1042 if (resp_data && dir & IPC_IOC_READ)
1043 memcpy(argp, resp_data, resp.data_len < size ?
1044 resp.data_len : size);
1045 free(resp_data);
1046
1047 return resp.sc_status;
1048}
1049
1050/*
1051 * Ask the IPC server for the h/w queues of the interface in an
1052 * ETHTOOL_GCHANNELS request. Any other ethtool command goes to the real
1053 * ioctl().
1054 */
1055static int handle_ethtool_ioctl(struct ifreq *ifr)
1056{
1057 struct ethtool_channels *chan = (void *)ifr->ifr_dataifr_ifru.ifru_data;
1058 struct ipc_response resp;
1059 uint32_t *counts;
1060 int ipc_fd;
1061
1062 if (!chan || chan->cmd != ETHTOOL_GCHANNELS0x0000003c)
1063 return -2;
1064
1065 ipc_fd = connect_ipc_socket();
1066 if (ipc_fd < 0)
1067 return -2;
1068
1069 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)
;
1070
1071 struct ipc_request req = {
1072 .type = IPC_TYPE_NIC_QUEUES,
1073 .data_len = strnlen(ifr->ifr_nameifr_ifrn.ifrn_name, sizeof(ifr->ifr_nameifr_ifrn.ifrn_name)),
1074 };
1075
1076 counts = ipc_call(ipc_fd, &req, ifr->ifr_nameifr_ifrn.ifrn_name, &resp);
1077 orig_close(ipc_fd);
1078
1079 if (resp.status == -1) {
1080 free(counts);
1081 errno(*__errno_location ()) = resp.errno_val;
1082 return -1;
1083 }
1084
1085 if (!counts || resp.data_len < 3 * sizeof(*counts)) {
1086 free(counts);
1087 errno(*__errno_location ()) = EIO5;
1088 return -1;
1089 }
1090
1091 chan->combined_count = le32toh(counts[0])__uint32_identity (counts[0]);
1092 chan->tx_count = le32toh(counts[1])__uint32_identity (counts[1]);
1093 chan->rx_count = le32toh(counts[2])__uint32_identity (counts[2]);
1094 free(counts);
1095
1096 return 0;
1097}
1098
1099static int mock_ioctl(int fd, unsigned long request, void *argp, orig_ioctl_t fallback)
1100{
1101 int instance = mock_ctrl_instance(fd);
1102 int ret;
1103
1104 if (request == SIOCETHTOOL0x8946 && argp) {
2
Assuming 'request' is not equal to SIOCETHTOOL
1105 ret = handle_ethtool_ioctl(argp);
1106 if (ret != -2)
1107 return ret;
1108 }
1109
1110 if (instance < 0)
3
Assuming 'instance' is >= 0
1111 return fallback(fd, request, argp);
1112
1113 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
1114 request == LIBNVME_IOCTL_ADMIN64_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x47)) << 0) | ((((sizeof(struct linux_passthru_cmd64
)))) << ((0 +8)+8)))
||
1115 request == LIBNVME_IOCTL_IO_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x43)) << 0) | ((((sizeof(struct linux_passthru_cmd32
)))) << ((0 +8)+8)))
||
1116 request == LIBNVME_IOCTL_IO64_CMD(((2U|1U) << (((0 +8)+8)+14)) | ((('N')) << (0 +8
)) | (((0x48)) << 0) | ((((sizeof(struct linux_passthru_cmd64
)))) << ((0 +8)+8)))
)
1117 ret = handle_passthru_ioctl(instance, request, argp);
5
Calling 'handle_passthru_ioctl'
1118 else
1119 ret = handle_raw_ioctl(instance, request, argp);
1120 if (ret != -2)
1121 return ret;
1122
1123 return fallback(fd, request, argp);
1124}
1125
1126int ioctl(int fd, unsigned long request, ...)
1127{
1128 va_list args;
1129 void *argp;
1130
1131 init_orig_functions();
1132
1133 va_start(args, request)__builtin_va_start(args, request);
1134 argp = va_arg(args, void *)__builtin_va_arg(args, void *);
1135 va_end(args)__builtin_va_end(args);
1136
1137 return mock_ioctl(fd, request, argp, orig_ioctl);
1138}
1139
1140int __ioctl(int fd, unsigned long request, ...)
1141{
1142 va_list args;
1143 void *argp;
1144
1145 va_start(args, request)__builtin_va_start(args, request);
1146 argp = va_arg(args, void *)__builtin_va_arg(args, void *);
1147 va_end(args)__builtin_va_end(args);
1148
1149 return ioctl(fd, request, argp);
1150}
1151
1152int __ioctl_time64(int fd, unsigned long request, ...)
1153{
1154 va_list args;
1155 void *argp;
1156
1157 init_orig_functions();
1158
1159 va_start(args, request)__builtin_va_start(args, request);
1160 argp = va_arg(args, void *)__builtin_va_arg(args, void *);
1161 va_end(args)__builtin_va_end(args);
1162
1163 return mock_ioctl(fd, request, argp, orig_ioctl_time64);
1
Calling 'mock_ioctl'
1164}
1165
1166int close(int fd)
1167{
1168 init_orig_functions();
1169
1170 if (fd == mock_fabrics_fd)
1171 mock_fabrics_fd = -1;
1172 if (fd == mock_fabrics_manager_fd)
1173 mock_fabrics_manager_fd = -1;
1174 if (fd == mock_netlink_fd) {
1175 mock_netlink_fd = -1;
1176 mock_route_reply.pending = false0;
1177 }
1178
1179 for (int i = 0; i < num_mock_ctrls; i++) {
1180 if (mock_ctrl_fds[i].fd != fd)
1181 continue;
1182 for (int j = i; j < num_mock_ctrls - 1; j++)
1183 mock_ctrl_fds[j] = mock_ctrl_fds[j + 1];
1184 num_mock_ctrls--;
1185 break;
1186 }
1187
1188 return orig_close(fd);
1189}
1190
1191int fstat(int fd, struct stat *buf)
1192{
1193 bool_Bool is_block;
1194
1195 init_orig_functions();
1196
1197 if (mock_ctrl_is_block(fd, &is_block)) {
1198 memset(buf, 0, sizeof(*buf));
1199 buf->st_mode = (is_block ? S_IFBLK0060000 : S_IFCHR0020000) | 0600;
1200 return 0;
1201 }
1202
1203 return orig_fstat(fd, buf);
1204}
1205
1206int fstat64(int fd, struct stat64 *buf)
1207{
1208 bool_Bool is_block;
1209
1210 init_orig_functions();
1211
1212 if (mock_ctrl_is_block(fd, &is_block)) {
1213 memset(buf, 0, sizeof(*buf));
1214 buf->st_mode = (is_block ? S_IFBLK0060000 : S_IFCHR0020000) | 0600;
1215 return 0;
1216 }
1217
1218 return orig_fstat64(fd, buf);
1219}
1220
1221int __fstat64_time64(int fd, void *buf)
1222{
1223 bool_Bool is_block;
1224 int ret;
1225
1226 init_orig_functions();
1227
1228 /*
1229 * buf's real layout is unknown here, so unlike fstat()/fstat64() this
1230 * can't memset() the whole thing and fabricate it from scratch. Let the
1231 * real call fill in a correctly-sized, correctly-shaped struct, then
1232 * only patch st_mode -- its offset is unaffected by the time64
1233 * transition.
1234 */
1235 ret = orig_fstat64_time64(fd, buf);
1236 if (ret == 0 && mock_ctrl_is_block(fd, &is_block))
1237 ((struct stat *)buf)->st_mode = (is_block ? S_IFBLK0060000 : S_IFCHR0020000) | 0600;
1238
1239 return ret;
1240}
1241
1242/*
1243 * Ask the IPC server for the answer to a password prompt, instead of
1244 * reading the terminal. Without an IPC server, fall back to the real
1245 * getpass().
1246 */
1247char *getpass(const char *prompt)
1248{
1249 static char pass[128];
1250 struct ipc_response resp;
1251 char *resp_data;
1252 size_t len;
1253 int ipc_fd;
1254
1255 init_orig_functions();
1256
1257 ipc_fd = connect_ipc_socket();
1258 if (ipc_fd < 0)
1259 return orig_getpass(prompt);
1260
1261 struct ipc_request req = {
1262 .type = IPC_TYPE_GETPASS,
1263 .data_len = strlen(prompt),
1264 };
1265
1266 resp_data = ipc_call(ipc_fd, &req, prompt, &resp);
1267 orig_close(ipc_fd);
1268
1269 if (resp.status == -1) {
1270 free(resp_data);
1271 errno(*__errno_location ()) = resp.errno_val;
1272 return NULL((void*)0);
1273 }
1274
1275 len = resp.data_len < sizeof(pass) - 1 ?
1276 resp.data_len : sizeof(pass) - 1;
1277 if (resp_data)
1278 memcpy(pass, resp_data, len);
1279 pass[len] = '\0';
1280 free(resp_data);
1281
1282 return pass;
1283}
1284
1285/*
1286 * Fake the NETLINK_ROUTE socket the egress route lookup uses. The fd is
1287 * a real (unconnected) socket, so close() and friends still work, but
1288 * sendmsg()/recvmsg() on it never reach the kernel.
1289 */
1290int socket(int domain, int type, int protocol)
1291{
1292 int fd;
1293
1294 init_orig_functions();
1295
1296 if (domain != AF_NETLINK16 || protocol != NETLINK_ROUTE0 ||
1297 !getenv("MOCK_IPC_SOCK"))
1298 return orig_socket(domain, type, protocol);
1299
1300 fd = orig_socket(AF_UNIX1, SOCK_DGRAMSOCK_DGRAM, 0);
1301 if (fd < 0)
1302 return fd;
1303
1304 mock_netlink_fd = fd;
1305 mock_route_reply.pending = false0;
1306 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)
;
1307
1308 return fd;
1309}
1310
1311static void rtattr_addr_to_str(struct rtattr *rta, unsigned char family,
1312 char *buf, size_t len)
1313{
1314 size_t alen = family == AF_INET2 ? sizeof(struct in_addr) :
1315 sizeof(struct in6_addr);
1316
1317 if (RTA_PAYLOAD(rta)((int)((rta)->rta_len) - (( ((sizeof(struct rtattr))+4U -1
) & ~(4U -1) ) + (0)))
< alen ||
1318 !inet_ntop(family, RTA_DATA(rta)((void*)(((char*)(rta)) + (( ((sizeof(struct rtattr))+4U -1) &
~(4U -1) ) + (0))))
, buf, len))
1319 buf[0] = '\0';
1320}
1321
1322/*
1323 * Decode an RTM_GETROUTE request and ask the IPC server for the egress
1324 * interface. Its answer is returned by the next recvmsg().
1325 */
1326static ssize_t mock_route_sendmsg(const struct msghdr *msg)
1327{
1328 char daddr[INET6_ADDRSTRLEN46] = "", saddr[INET6_ADDRSTRLEN46] = "";
1329 char payload[2 * INET6_ADDRSTRLEN46];
1330 struct ipc_response resp;
1331 struct nlmsghdr *nlh;
1332 struct rtmsg *rtm;
1333 struct rtattr *rta;
1334 char *ifname;
1335 int attrlen, ipc_fd;
1336 size_t len;
1337
1338 if (!msg->msg_iovlen ||
1339 msg->msg_iov[0].iov_len < NLMSG_LENGTH(sizeof(*rtm))((sizeof(*rtm)) + ((int) ( ((sizeof(struct nlmsghdr))+4U -1) &
~(4U -1) )))
) {
1340 errno(*__errno_location ()) = EINVAL22;
1341 return -1;
1342 }
1343
1344 nlh = msg->msg_iov[0].iov_base;
1345 if (nlh->nlmsg_type != RTM_GETROUTERTM_GETROUTE ||
1346 nlh->nlmsg_len > msg->msg_iov[0].iov_len) {
1347 errno(*__errno_location ()) = EOPNOTSUPP95;
1348 return -1;
1349 }
1350
1351 rtm = NLMSG_DATA(nlh)((void *)(((char *)nlh) + ((int) ( ((sizeof(struct nlmsghdr))
+4U -1) & ~(4U -1) ))))
;
1352 rta = RTM_RTA(rtm)((struct rtattr*)(((char*)(rtm)) + ( ((sizeof(struct rtmsg))+
4U -1) & ~(4U -1) )))
;
1353 attrlen = RTM_PAYLOAD(nlh)((nlh)->nlmsg_len - ( (((((sizeof(struct rtmsg))) + ((int)
( ((sizeof(struct nlmsghdr))+4U -1) & ~(4U -1) ))))+4U -
1) & ~(4U -1) ))
;
1354 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) )))
) {
1355 if (rta->rta_type == RTA_DST)
1356 rtattr_addr_to_str(rta, rtm->rtm_family, daddr,
1357 sizeof(daddr));
1358 else if (rta->rta_type == RTA_SRC)
1359 rtattr_addr_to_str(rta, rtm->rtm_family, saddr,
1360 sizeof(saddr));
1361 }
1362
1363 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)
1364 daddr, saddr)do { if (mock_debug) fprintf(stderr, "[MOCK_DEBUG] " "route lookup intercepted, daddr '%s', saddr '%s'\n"
, daddr, saddr); } while (0)
;
1365
1366 mock_route_reply.req = *nlh;
1367 mock_route_reply.family = rtm->rtm_family;
1368 mock_route_reply.error = 0;
1369 mock_route_reply.pending = true1;
1370
1371 ipc_fd = connect_ipc_socket();
1372 if (ipc_fd < 0) {
1373 mock_route_reply.error = EIO5;
1374 return nlh->nlmsg_len;
1375 }
1376
1377 len = strlen(daddr) + 1;
1378 memcpy(payload, daddr, len);
1379 memcpy(payload + len, saddr, strlen(saddr));
1380 len += strlen(saddr);
1381
1382 struct ipc_request req = {
1383 .type = IPC_TYPE_ROUTE,
1384 .data_len = len,
1385 };
1386
1387 ifname = ipc_call(ipc_fd, &req, payload, &resp);
1388 orig_close(ipc_fd);
1389
1390 if (resp.status == -1) {
1391 mock_route_reply.error = resp.errno_val;
1392 } else if (!ifname || !resp.data_len ||
1393 resp.data_len >= sizeof(mock_ifname)) {
1394 mock_route_reply.error = EIO5;
1395 } else {
1396 memcpy(mock_ifname, ifname, resp.data_len);
1397 mock_ifname[resp.data_len] = '\0';
1398 }
1399 free(ifname);
1400
1401 return nlh->nlmsg_len;
1402}
1403
1404/*
1405 * Build the kernel's answer to the pending RTM_GETROUTE request: an
1406 * RTM_NEWROUTE with the egress interface in RTA_OIF, or an NLMSG_ERROR.
1407 */
1408static ssize_t mock_route_recvmsg(struct msghdr *msg)
1409{
1410 struct nlmsghdr *nlh;
1411 size_t len;
1412
1413 if (!mock_route_reply.pending) {
1414 errno(*__errno_location ()) = EAGAIN11;
1415 return -1;
1416 }
1417 mock_route_reply.pending = false0;
1418
1419 if (mock_route_reply.error)
1420 len = NLMSG_LENGTH(sizeof(struct nlmsgerr))((sizeof(struct nlmsgerr)) + ((int) ( ((sizeof(struct nlmsghdr
))+4U -1) & ~(4U -1) )))
;
1421 else
1422 len = NLMSG_LENGTH(sizeof(struct rtmsg))((sizeof(struct rtmsg)) + ((int) ( ((sizeof(struct nlmsghdr))
+4U -1) & ~(4U -1) )))
+
1423 RTA_SPACE(sizeof(int))( (((( ((sizeof(struct rtattr))+4U -1) & ~(4U -1) ) + (sizeof
(int))))+4U -1) & ~(4U -1) )
;
1424
1425 if (!msg->msg_iovlen || msg->msg_iov[0].iov_len < len) {
1426 errno(*__errno_location ()) = ENOBUFS105;
1427 return -1;
1428 }
1429
1430 nlh = msg->msg_iov[0].iov_base;
1431 memset(nlh, 0, len);
1432 nlh->nlmsg_len = len;
1433 nlh->nlmsg_seq = mock_route_reply.req.nlmsg_seq;
1434
1435 if (mock_route_reply.error) {
1436 struct nlmsgerr *err = NLMSG_DATA(nlh)((void *)(((char *)nlh) + ((int) ( ((sizeof(struct nlmsghdr))
+4U -1) & ~(4U -1) ))))
;
1437
1438 nlh->nlmsg_type = NLMSG_ERROR0x2;
1439 err->error = -mock_route_reply.error;
1440 err->msg = mock_route_reply.req;
1441 } else {
1442 struct rtmsg *rtm = NLMSG_DATA(nlh)((void *)(((char *)nlh) + ((int) ( ((sizeof(struct nlmsghdr))
+4U -1) & ~(4U -1) ))))
;
1443 struct rtattr *rta = RTM_RTA(rtm)((struct rtattr*)(((char*)(rtm)) + ( ((sizeof(struct rtmsg))+
4U -1) & ~(4U -1) )))
;
1444 int ifindex = MOCK_IFINDEX0x4d4f434b;
1445
1446 nlh->nlmsg_type = RTM_NEWROUTERTM_NEWROUTE;
1447 rtm->rtm_family = mock_route_reply.family;
1448 rta->rta_type = RTA_OIF;
1449 rta->rta_len = RTA_LENGTH(sizeof(ifindex))(( ((sizeof(struct rtattr))+4U -1) & ~(4U -1) ) + (sizeof
(ifindex)))
;
1450 memcpy(RTA_DATA(rta)((void*)(((char*)(rta)) + (( ((sizeof(struct rtattr))+4U -1) &
~(4U -1) ) + (0))))
, &ifindex, sizeof(ifindex));
1451 }
1452
1453 return len;
1454}
1455
1456ssize_t sendmsg(int fd, const struct msghdr *msg, int flags)
1457{
1458 init_orig_functions();
1459
1460 if (fd == mock_netlink_fd)
1461 return mock_route_sendmsg(msg);
1462
1463 return orig_sendmsg(fd, msg, flags);
1464}
1465
1466ssize_t __sendmsg64(int fd, const struct msghdr *msg, int flags)
1467{
1468 init_orig_functions();
1469
1470 if (fd == mock_netlink_fd)
1471 return mock_route_sendmsg(msg);
1472
1473 return orig_sendmsg64(fd, msg, flags);
1474}
1475
1476ssize_t recvmsg(int fd, struct msghdr *msg, int flags)
1477{
1478 init_orig_functions();
1479
1480 if (fd == mock_netlink_fd)
1481 return mock_route_recvmsg(msg);
1482
1483 return orig_recvmsg(fd, msg, flags);
1484}
1485
1486ssize_t __recvmsg64(int fd, struct msghdr *msg, int flags)
1487{
1488 init_orig_functions();
1489
1490 if (fd == mock_netlink_fd)
1491 return mock_route_recvmsg(msg);
1492
1493 return orig_recvmsg64(fd, msg, flags);
1494}
1495
1496char *if_indextoname(unsigned int ifindex, char ifname[IF_NAMESIZE16])
1497{
1498 init_orig_functions();
1499
1500 if (ifindex != MOCK_IFINDEX0x4d4f434b || !mock_ifname[0])
1501 return orig_if_indextoname(ifindex, ifname);
1502
1503 memcpy(ifname, mock_ifname, sizeof(mock_ifname));
1504
1505 return ifname;
1506}
1507
1508/*
1509 * Ask the IPC server for the number of online CPUs. Without an IPC
1510 * server, or if it doesn't set one, fall back to the real get_nprocs().
1511 */
1512int get_nprocs(void)
1513{
1514 struct ipc_response resp;
1515 void *resp_data;
1516 int ipc_fd;
1517
1518 init_orig_functions();
1519
1520 ipc_fd = connect_ipc_socket();
1521 if (ipc_fd < 0)
1522 return orig_get_nprocs();
1523
1524 struct ipc_request req = {
1525 .type = IPC_TYPE_NPROCS,
1526 };
1527
1528 resp_data = ipc_call(ipc_fd, &req, NULL((void*)0), &resp);
1529 orig_close(ipc_fd);
1530 free(resp_data);
1531
1532 if (resp.status == -1)
1533 return orig_get_nprocs();
1534
1535 return resp.result;
1536}