Bug Summary

File:.build-ci/../util/utils.c
Warning:line 131, column 3
Potential leak of memory pointed to by 'buffer'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name utils.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 -pic-is-pie -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/lib/llvm-19/lib/clang/19 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -std=gnu99 -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 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-06-24-175442-590-1 -x c ../util/utils.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) Micron, Inc 2024.
4 *
5 * @file: micron-utils.h
6 * @brief: This module contains all the utilities needed for other modules.
7 * @author: Chaithanya Shoba <ashoba@micron.com>
8 */
9
10#include "utils.h"
11#include "types.h"
12#include "json.h"
13#include "cleanup.h"
14
15int hex_to_int(char c)
16{
17 if (c >= '0' && c <= '9')
18 return c - '0';
19 else if (c >= 'A' && c <= 'F')
20 return 10 + (c - 'A');
21 else if (c >= 'a' && c <= 'f')
22 return 10 + (c - 'a');
23 else
24 return -1; // Invalid character
25}
26
27char *hex_to_ascii(const char *hex)
28{
29 int hex_length = strlen(hex);
30
31 char *text = NULL((void*)0);
32
33 if (hex_length > 0) {
34 int symbol_count;
35 int odd_hex_count = hex_length % 2 == 1;
36
37 if (odd_hex_count)
38 symbol_count = (hex_length / 2) + 1;
39 else
40 symbol_count = hex_length / 2;
41
42 text = (char *)malloc(symbol_count + 1); // Allocate memory for the result
43
44 int last_index = hex_length - 1;
45
46 for (int i = last_index; i >= 0; --i) {
47 if ((last_index - i) % 2 != 0) {
48 int dec = 16 * hex_to_int(hex[i]) + hex_to_int(hex[i + 1]);
49
50 if (odd_hex_count)
51 text[i / 2 + 1] = dec;
52 else
53 text[i / 2] = dec;
54 } else if (i == 0) {
55 int dec = hex_to_int(hex[0]);
56
57 text[0] = dec;
58 }
59 }
60
61 text[symbol_count] = '\0'; // Terminate the string
62 }
63
64 return text;
65}
66
67unsigned char *read_binary_file(char *data_dir_path, const char *bin_path,
68 long *buffer_size, int retry_count)
69{
70 char *file_path = NULL((void*)0);
71 FILE *bin_file = NULL((void*)0);
72 size_t n_data = 0;
73 unsigned char *buffer = NULL((void*)0);
74
75 /* set path */
76 if (data_dir_path == NULL((void*)0)) {
1
Assuming 'data_dir_path' is equal to NULL
2
Taking true branch
77 file_path = (char *)bin_path;
78 } else {
79 /* +2 for the / and null terminator */
80 file_path = (char *) calloc(1, strlen(data_dir_path) + strlen(bin_path) + 2);
81 if (!file_path)
82 return NULL((void*)0);
83
84 if (strlen(bin_path) != 0)
85 sprintf(file_path, "%s/%s", data_dir_path, bin_path);
86 else
87 sprintf(file_path, "%s", data_dir_path);
88 }
89
90 /* open file */
91 for (int i = 0; i < retry_count; i++) {
3
Assuming 'i' is < 'retry_count'
4
Loop condition is true. Entering loop body
92 bin_file = fopen(file_path, "rb");
93 if (bin_file
4.1
'bin_file' is not equal to NULL
!= NULL((void*)0))
5
Taking true branch
94 break;
95 sleep((unsigned int)(retry_count > 1));
96 }
97
98 if (!bin_file
6.1
'bin_file' is non-null
) {
6
Execution continues on line 98
7
Taking false branch
99 nvme_show_error("\nFailed to open %s", file_path)nvme_show_message(1, "\nFailed to open %s", file_path);
100 if (file_path != bin_path)
101 free(file_path);
102 return NULL((void*)0);
103 }
104
105 /* get size */
106 fseek(bin_file, 0, SEEK_END2);
107 *buffer_size = ftell(bin_file);
108 fseek(bin_file, 0, SEEK_SET0);
109 if (*buffer_size <= 0) {
8
Assuming the condition is false
9
Taking false branch
110 fclose(bin_file);
111 return NULL((void*)0);
112 }
113
114 /* allocate buffer */
115 buffer = (unsigned char *)malloc(*buffer_size);
10
Memory is allocated
116 if (!buffer) {
11
Assuming 'buffer' is non-null
12
Taking false branch
117 nvme_show_result("\nFailed to allocate %ld bytes!", *buffer_size)nvme_show_message(0, "\nFailed to allocate %ld bytes!", *buffer_size
)
;
118 fclose(bin_file);
119 return NULL((void*)0);
120 }
121 memset(buffer, 0, *buffer_size);
122
123 /* Read data */
124 n_data = fread(buffer, 1, *buffer_size, bin_file);
125
126 /* Close file */
127 fclose(bin_file);
128
129 /* Validate we read data */
130 if (n_data != (size_t)*buffer_size) {
13
Assuming the condition is true
14
Taking true branch
131 nvme_show_result("\nFailed to read %ld bytes from %s", *buffer_size, file_path)nvme_show_message(0, "\nFailed to read %ld bytes from %s", *buffer_size
, file_path)
;
15
Potential leak of memory pointed to by 'buffer'
132 return NULL((void*)0);
133 }
134
135 if (file_path != bin_path)
136 free(file_path);
137 return buffer;
138}
139
140void print_formatted_var_size_str(const char *msg, const __u8 *pdata, size_t data_size, FILE *fp)
141{
142 char *description_str = NULL((void*)0);
143 char temp_buffer[3] = { 0 };
144
145 /* Allocate 2 chars for each value in the data + 2 bytes for the null terminator */
146 description_str = (char *) calloc(1, data_size*2 + 2);
147
148 for (size_t i = 0; i < data_size; ++i) {
149 sprintf(temp_buffer, "%02X", pdata[i]);
150 strcat(description_str, temp_buffer);
151 }
152
153 if (!fp)
154 fp = stdoutstdout;
155
156 fprintf(fp, "%s: %s\n", msg, description_str);
157 free(description_str);
158}
159
160char *process_field_size_16(int offset, char *sfield, __u8 *buf)
161{
162 __u64 lval_lo, lval_hi;
163 char *datastr = NULL((void*)0);
164
165 if (strstr(sfield, "GUID")) {
166 if (asprintf(&datastr, "0x%"PRIx64"l" "x""%"PRIx64"l" "x""",
167 le64_to_cpu(*(__u64 *)(&buf[offset + 8])),
168 le64_to_cpu(*(__u64 *)(&buf[offset]))) < 0)
169 return NULL((void*)0);
170 } else {
171 lval_lo = *((__u64 *)(&buf[offset]));
172 lval_hi = *((__u64 *)(&buf[offset + 8]));
173
174 if (lval_hi) {
175 if (asprintf(&datastr, "0x%"PRIx64"l" "x""%016"PRIx64"l" "x""",
176 le64_to_cpu(lval_hi), le64_to_cpu(lval_lo)) < 0)
177 return NULL((void*)0);
178 } else {
179 if (asprintf(&datastr, "0x%"PRIx64"l" "x""", le64_to_cpu(lval_lo)) < 0)
180 return NULL((void*)0);
181 }
182 }
183 return datastr;
184}
185
186char *process_field_size_8(int offset, char *sfield, __u8 *buf)
187{
188 __u64 lval_lo;
189 char *datastr = NULL((void*)0);
190
191 if (strstr(sfield, "Boot SSD Spec Version")) {
192 if (asprintf(&datastr, "%x.%x.%x.%x",
193 le16_to_cpu(*((__u16 *)(&buf[300]))),
194 le16_to_cpu(*((__u16 *)(&buf[302]))),
195 le16_to_cpu(*((__u16 *)(&buf[304]))),
196 le16_to_cpu(*((__u16 *)(&buf[306])))) < 0)
197 return NULL((void*)0);
198 } else if (strstr(sfield, "Firmware Revision")) {
199 char buffer[30] = {'\0'};
200
201 lval_lo = *((__u64 *)(&buf[offset]));
202
203 sprintf(buffer, "%"PRIx64"l" "x", __builtin_bswap64(lval_lo));
204 datastr = hex_to_ascii(buffer);
205 } else if (strstr(sfield, "Timestamp")) {
206 char ts_buf[128];
207
208 lval_lo = *((__u64 *)(&buf[offset]));
209
210 convert_ts(le64_to_cpu(lval_lo), ts_buf);
211 datastr = strdup(ts_buf);
212 } else {
213 lval_lo = *((__u64 *)(&buf[offset]));
214
215 if (asprintf(&datastr, "0x%"PRIx64"l" "x""", le64_to_cpu(lval_lo)) < 0)
216 return NULL((void*)0);
217 }
218 return datastr;
219}
220
221char *process_field_size_7(int offset, char *sfield, __u8 *buf)
222{
223 __u8 lval[8] = { 0 };
224 __u64 lval_lo;
225 char *datastr = NULL((void*)0);
226
227 /* 7 bytes will be in little-endian format, with last byte as MSB */
228 memcpy(&lval[0], &buf[offset], 7);
229 memcpy((void *)&lval_lo, lval, 8);
230 if (asprintf(&datastr, "0x%"PRIx64"l" "x""", le64_to_cpu(lval_lo)) < 0)
231 return NULL((void*)0);
232 return datastr;
233}
234
235char *process_field_size_6(int offset, char *sfield, __u8 *buf)
236{
237 __u32 ival;
238 __u16 sval;
239 __u64 lval_lo;
240 char *datastr = NULL((void*)0);
241
242 if (strstr(sfield, "DSSD Spec Version")) {
243 if (asprintf(&datastr, "%x.%x.%x.%x", buf[103],
244 le16_to_cpu(*((__u16 *)(&buf[101]))),
245 le16_to_cpu(*((__u16 *)(&buf[99]))), buf[98]) < 0)
246 return NULL((void*)0);
247 } else {
248 ival = *((__u32 *)(&buf[offset]));
249 sval = *((__u16 *)(&buf[offset + 4]));
250 lval_lo = (((__u64)sval << 32) | ival);
251
252 if (asprintf(&datastr, "0x%"PRIx64"l" "x""", le64_to_cpu(lval_lo)) < 0)
253 return NULL((void*)0);
254 }
255 return datastr;
256}
257
258char *process_field_size_default(int offset, char *sfield, __u8 *buf, int size)
259{
260 /* "0x" prefix + 2 hex chars per byte + null terminator */
261 char *datastr = malloc(size * 2 + 3);
262
263 if (!datastr)
264 return NULL((void*)0);
265
266 datastr[0] = '0';
267 datastr[1] = 'x';
268 for (int i = 0; i < size; i++)
269 sprintf(&datastr[2 + i * 2], "%02X", buf[offset + i]);
270
271 return datastr;
272}
273
274void generic_structure_parser(__u8 *buf, struct request_data *req_data, int field_count,
275 struct json_object *stats, __u8 spec, FILE *fp)
276{
277 int offset = 0;
278
279 for (int field = 0; field < field_count; field++) {
280 __cleanup_free__attribute__((cleanup(freep))) char *datastr = NULL((void*)0);
281 char *sfield = req_data[field].field;
282 int size = !spec ? req_data[field].size : req_data[field].size2;
283
284 if (!size || sfield == NULL((void*)0))
285 continue;
286
287 switch (size) {
288 case FIELD_SIZE_16:
289 datastr = process_field_size_16(offset, sfield, buf);
290 break;
291 case FIELD_SIZE_8:
292 datastr = process_field_size_8(offset, sfield, buf);
293 break;
294 case FIELD_SIZE_7:
295 datastr = process_field_size_7(offset, sfield, buf);
296 break;
297 case FIELD_SIZE_6:
298 datastr = process_field_size_6(offset, sfield, buf);
299 break;
300 case FIELD_SIZE_4:
301 if (asprintf(&datastr, "0x%x",
302 le32_to_cpu(*((__u32 *)(&buf[offset])))) < 0)
303 datastr = NULL((void*)0);
304 break;
305 case FIELD_SIZE_3:
306 if (asprintf(&datastr, "0x%02X%02X%02X",
307 buf[offset + 0], buf[offset + 1],
308 buf[offset + 2]) < 0)
309 datastr = NULL((void*)0);
310 break;
311 case FIELD_SIZE_2:
312 if (asprintf(&datastr, "0x%04x",
313 le16_to_cpu(*((__u16 *)(&buf[offset])))) < 0)
314 datastr = NULL((void*)0);
315 break;
316 case FIELD_SIZE_1:
317 if (asprintf(&datastr, "0x%02x", buf[offset]) < 0)
318 datastr = NULL((void*)0);
319 break;
320 default:
321 datastr = process_field_size_default(offset, sfield, buf, size);
322 break;
323 }
324 offset += size;
325 /* do not print reserved values */
326 if (strstr(sfield, "Reserved"))
327 continue;
328 if (datastr) {
329 if (stats)
330 json_object_add_value_string(stats, sfield, datastr);
331 else if (fp)
332 fprintf(fp, "%-40s : %-4s\n", sfield, datastr);
333 else
334 printf("%-40s : %-4s\n", sfield, datastr);
335 }
336 }
337}