Bug Summary

File:.build-ci/../tests/unit/c/test-regs.c
Warning:line 115, column 10
Null pointer passed to 1st parameter expecting 'nonnull'

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 test-regs.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I tests/unit/c/test-regs.p -I tests/unit/c -I ../tests/unit/c -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -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/unit/c/test-regs.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * This file is part of nvme-cli.
4 * Copyright (c) 2026 SUSE LLC
5 *
6 * Authors: Daniel Wagner <dwagner@suse.de>
7 */
8
9/*
10 * Unit tests for nvme-regs.c: the consistency of the register table, and
11 * which registers are properties of a fabrics controller and which the
12 * host may write (NVM Express Base Specification 2.4, Figure 33).
13 */
14
15#include <stdbool.h>
16#include <stdint.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21#include <ccan/array_size/array_size.h>
22#include <libnvme.h>
23
24#include "../src/nvme-regs.h"
25
26static bool_Bool report(const char *what, bool_Bool pass)
27{
28 printf(" - %s [%s]\n", what, pass ? "PASS" : "FAIL");
29 return pass;
30}
31
32static int mask_bits(uint64_t mask)
33{
34 int n = 0;
35
36 while (mask & 1) {
37 mask >>= 1;
38 n++;
39 }
40 return mask ? -1 : n; /* -1: not contiguous from bit 0 */
41}
42
43static bool_Bool test_layout(void)
44{
45 bool_Bool order = true1, size = true1, unique = true1, pass;
46 size_t i, j;
47
48 printf("register table layout\n");
49 for (i = 0; i < ARRAY_SIZE(nvme_regs)(sizeof(nvme_regs) / sizeof((nvme_regs)[0]) + 0); i++) {
50 const struct nvme_reg *r = &nvme_regs[i];
51
52 if (i && r->offset <= nvme_regs[i - 1].offset) {
53 printf(" %s out of order\n", r->sym);
54 order = false0;
55 }
56 if (r->size != (nvme_is_64bit_reg(r->offset) ? 8 : 4)) {
57 printf(" %s: size %u\n", r->sym, r->size);
58 size = false0;
59 }
60 for (j = 0; j < i; j++)
61 if (!strcmp(r->sym, nvme_regs[j].sym) ||
62 !strcmp(r->name, nvme_regs[j].name)) {
63 printf(" %s twice\n", r->sym);
64 unique = false0;
65 }
66 }
67
68 pass = report("ascending offsets", order);
69 pass &= report("size of the 64 bit registers", size);
70 pass &= report("unique names", unique);
71
72 return pass;
73}
74
75static bool_Bool test_fields(void)
76{
77 bool_Bool inside = true1, overlap = true1, strs = true1, names = true1, pass;
78 size_t i;
79
80 printf("register fields\n");
81 for (i = 0; i < ARRAY_SIZE(nvme_regs)(sizeof(nvme_regs) / sizeof((nvme_regs)[0]) + 0); i++) {
2
Loop condition is true. Entering loop body
82 const struct nvme_reg *r = &nvme_regs[i];
83 uint64_t used = 0;
84 unsigned int f, g;
85
86 if (!r->nr_fields) {
3
Assuming field 'nr_fields' is not equal to 0
4
Taking false branch
87 printf(" %s: no fields\n", r->sym);
88 inside = false0;
89 }
90 for (f = 0; f
4.1
'f' is < field 'nr_fields'
< r->nr_fields
; f++) {
5
Loop condition is true. Entering loop body
7
Assuming 'f' is < field 'nr_fields'
8
Loop condition is true. Entering loop body
91 const struct bitfield *b = &r->fields[f];
92 int bits = mask_bits(b->mask);
93 uint64_t m;
94
95 if (bits
5.1
'bits' is <= 0
<= 0
|| b->shift + bits > r->size * 8) {
9
Assuming 'bits' is > 0
10
Assuming the condition is false
96 printf(" %s: %s outside\n", r->sym, b->name);
97 inside = false0;
98 continue;
6
Execution continues on line 90
99 }
100 m = (bits == 64 ? UINT64_MAX(18446744073709551615UL) :
11
Taking false branch
12
Assuming 'bits' is equal to 64
13
'?' condition is true
14
Assuming right operand of bit shift is less than 64
101 (UINT64_C(1)1UL << bits) - 1) << b->shift;
102 if (used & m) {
15
Taking false branch
103 printf(" %s: %s overlaps\n", r->sym, b->name);
104 overlap = false0;
105 }
106 used |= m;
107 if (b->fmt == BF_STR &&
16
Assuming field 'fmt' is not equal to BF_STR
108 (!b->nr_strs || b->nr_strs - 1 > b->mask)) {
109 printf(" %s: %s strings\n", r->sym, b->name);
110 strs = false0;
111 }
112 if (!b->name || !b->name[0])
17
Assuming field 'name' is null
113 names = false0;
114 for (g = 0; g < f; g++)
18
Loop condition is true. Entering loop body
115 if (!strcmp(b->name, r->fields[g].name)) {
19
Null pointer passed to 1st parameter expecting 'nonnull'
116 printf(" %s: %s twice\n", r->sym,
117 b->name);
118 names = false0;
119 }
120 }
121 }
122
123 pass = report("fields inside their register", inside);
124 pass &= report("no overlapping fields", overlap);
125 pass &= report("string tables fit their field", strs);
126 pass &= report("unique field names", names);
127
128 return pass;
129}
130
131static bool_Bool flags_are(const char *what, unsigned int flag,
132 const enum nvme_reg_idx *expected, size_t n)
133{
134 bool_Bool pass = true1;
135 size_t i, j;
136
137 for (i = 0; i < ARRAY_SIZE(nvme_regs)(sizeof(nvme_regs) / sizeof((nvme_regs)[0]) + 0); i++) {
138 bool_Bool want = false0;
139
140 for (j = 0; j < n; j++)
141 want |= expected[j] == i;
142 if (!!(nvme_regs[i].flags & flag) != want) {
143 printf(" %s: %s\n", what, nvme_regs[i].sym);
144 pass = false0;
145 }
146 }
147 return report(what, pass);
148}
149
150static bool_Bool test_flags(void)
151{
152 static const enum nvme_reg_idx fabrics[] = {
153 NVME_REGI_CAP, NVME_REGI_VS, NVME_REGI_CC, NVME_REGI_CSTS,
154 NVME_REGI_NSSR, NVME_REGI_NSSD, NVME_REGI_CRTO,
155 };
156 static const enum nvme_reg_idx optional[] = {
157 NVME_REGI_NSSR, NVME_REGI_NSSD, NVME_REGI_CRTO,
158 };
159 static const enum nvme_reg_idx write[] = {
160 NVME_REGI_INTMS, NVME_REGI_INTMC, NVME_REGI_CC, NVME_REGI_CSTS,
161 NVME_REGI_NSSR, NVME_REGI_AQA, NVME_REGI_ASQ, NVME_REGI_ACQ,
162 NVME_REGI_BPRSEL, NVME_REGI_BPMBL, NVME_REGI_CMBMSC,
163 NVME_REGI_NSSD, NVME_REGI_PMRCTL, NVME_REGI_PMRMSCL,
164 NVME_REGI_PMRMSCU,
165 };
166
167 bool_Bool pass;
168
169 printf("register flags\n");
170 pass = flags_are("fabrics properties", NVME_REGF_FABRICS, fabrics,
171 ARRAY_SIZE(fabrics)(sizeof(fabrics) / sizeof((fabrics)[0]) + 0));
172 pass &= flags_are("optional fabrics properties", NVME_REGF_OPTIONAL,
173 optional, ARRAY_SIZE(optional)(sizeof(optional) / sizeof((optional)[0]) + 0));
174 pass &= flags_are("host writable registers", NVME_REGF_WRITE, write,
175 ARRAY_SIZE(write)(sizeof(write) / sizeof((write)[0]) + 0));
176
177 return pass;
178}
179
180static bool_Bool test_by_offset(void)
181{
182 bool_Bool found = true1, pass;
183 size_t i;
184
185 printf("nvme_reg_by_offset\n");
186 for (i = 0; i < ARRAY_SIZE(nvme_regs)(sizeof(nvme_regs) / sizeof((nvme_regs)[0]) + 0); i++)
187 found &= nvme_reg_by_offset(nvme_regs[i].offset) ==
188 &nvme_regs[i];
189
190 pass = report("each register", found);
191 pass &= report("reserved and unaligned offsets",
192 !nvme_reg_by_offset(0x18) &&
193 !nvme_reg_by_offset(0x2) &&
194 !nvme_reg_by_offset(0x6c) &&
195 !nvme_reg_by_offset(0x1000));
196
197 return pass;
198}
199
200int main(void)
201{
202 bool_Bool pass = true1;
203
204 pass &= test_layout();
205 pass &= test_fields();
1
Calling 'test_fields'
206 pass &= test_flags();
207 pass &= test_by_offset();
208
209 fflush(stdoutstdout);
210 exit(pass ? EXIT_SUCCESS0 : EXIT_FAILURE1);
211}