Bug Summary

File:build-scan/../src/import/qcow2-util.c
Warning:line 265, column 25
Potential leak of memory pointed to by 'buffer2'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name qcow2-util.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I test-qcow2.p -I . -I .. -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/import/qcow2-util.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <zlib.h>
4
5#include "alloc-util.h"
6#include "btrfs-util.h"
7#include "qcow2-util.h"
8#include "sparse-endian.h"
9#include "util.h"
10
11#define QCOW2_MAGIC0x514649fb 0x514649fb
12
13#define QCOW2_COPIED(1ULL << 63) (1ULL << 63)
14#define QCOW2_COMPRESSED(1ULL << 62) (1ULL << 62)
15#define QCOW2_ZERO(1ULL << 0) (1ULL << 0)
16
17typedef struct _packed___attribute__ ((packed)) Header {
18 be32_t magic;
19 be32_t version;
20
21 be64_t backing_file_offset;
22 be32_t backing_file_size;
23
24 be32_t cluster_bits;
25 be64_t size;
26 be32_t crypt_method;
27
28 be32_t l1_size;
29 be64_t l1_table_offset;
30
31 be64_t refcount_table_offset;
32 be32_t refcount_table_clusters;
33
34 be32_t nb_snapshots;
35 be64_t snapshots_offset;
36
37 /* The remainder is only present on QCOW3 */
38 be64_t incompatible_features;
39 be64_t compatible_features;
40 be64_t autoclear_features;
41
42 be32_t refcount_order;
43 be32_t header_length;
44} Header;
45
46#define HEADER_MAGIC(header)be32toh((header)->magic) be32toh((header)->magic)
47#define HEADER_VERSION(header)be32toh((header)->version) be32toh((header)->version)
48#define HEADER_CLUSTER_BITS(header)be32toh((header)->cluster_bits) be32toh((header)->cluster_bits)
49#define HEADER_CLUSTER_SIZE(header)(1ULL << be32toh((header)->cluster_bits)) (1ULL << HEADER_CLUSTER_BITS(header)be32toh((header)->cluster_bits))
50#define HEADER_L2_BITS(header)(be32toh((header)->cluster_bits) - 3) (HEADER_CLUSTER_BITS(header)be32toh((header)->cluster_bits) - 3)
51#define HEADER_SIZE(header)be64toh((header)->size) be64toh((header)->size)
52#define HEADER_CRYPT_METHOD(header)be32toh((header)->crypt_method) be32toh((header)->crypt_method)
53#define HEADER_L1_SIZE(header)be32toh((header)->l1_size) be32toh((header)->l1_size)
54#define HEADER_L2_SIZE(header)((1ULL << be32toh((header)->cluster_bits))/sizeof(uint64_t
))
(HEADER_CLUSTER_SIZE(header)(1ULL << be32toh((header)->cluster_bits))/sizeof(uint64_t))
55#define HEADER_L1_TABLE_OFFSET(header)be64toh((header)->l1_table_offset) be64toh((header)->l1_table_offset)
56
57static uint32_t HEADER_HEADER_LENGTH(const Header *h) {
58 if (HEADER_VERSION(h)be32toh((h)->version) < 3)
59 return offsetof(Header, incompatible_features)__builtin_offsetof(Header, incompatible_features);
60
61 return be32toh(h->header_length);
62}
63
64static int copy_cluster(
65 int sfd, uint64_t soffset,
66 int dfd, uint64_t doffset,
67 uint64_t cluster_size,
68 void *buffer) {
69
70 ssize_t l;
71 int r;
72
73 r = btrfs_clone_range(sfd, soffset, dfd, doffset, cluster_size);
74 if (r >= 0)
75 return r;
76
77 l = pread(sfd, buffer, cluster_size, soffset);
78 if (l < 0)
79 return -errno(*__errno_location ());
80 if ((uint64_t) l != cluster_size)
81 return -EIO5;
82
83 l = pwrite(dfd, buffer, cluster_size, doffset);
84 if (l < 0)
85 return -errno(*__errno_location ());
86 if ((uint64_t) l != cluster_size)
87 return -EIO5;
88
89 return 0;
90}
91
92static int decompress_cluster(
93 int sfd, uint64_t soffset,
94 int dfd, uint64_t doffset,
95 uint64_t compressed_size,
96 uint64_t cluster_size,
97 void *buffer1,
98 void *buffer2) {
99
100 _cleanup_free___attribute__((cleanup(freep))) void *large_buffer = NULL((void*)0);
101 z_stream s = {};
102 uint64_t sz;
103 ssize_t l;
104 int r;
105
106 if (compressed_size > cluster_size) {
107 /* The usual cluster buffer doesn't suffice, let's
108 * allocate a larger one, temporarily */
109
110 large_buffer = malloc(compressed_size);
111 if (!large_buffer)
112 return -ENOMEM12;
113
114 buffer1 = large_buffer;
115 }
116
117 l = pread(sfd, buffer1, compressed_size, soffset);
118 if (l < 0)
119 return -errno(*__errno_location ());
120 if ((uint64_t) l != compressed_size)
121 return -EIO5;
122
123 s.next_in = buffer1;
124 s.avail_in = compressed_size;
125 s.next_out = buffer2;
126 s.avail_out = cluster_size;
127
128 r = inflateInit2(&s, -12)inflateInit2_((&s), (-12), "1.2.11", (int)sizeof(z_stream
))
;
129 if (r != Z_OK0)
130 return -EIO5;
131
132 r = inflate(&s, Z_FINISH4);
133 sz = (uint8_t*) s.next_out - (uint8_t*) buffer2;
134 inflateEnd(&s);
135 if (r != Z_STREAM_END1 || sz != cluster_size)
136 return -EIO5;
137
138 l = pwrite(dfd, buffer2, cluster_size, doffset);
139 if (l < 0)
140 return -errno(*__errno_location ());
141 if ((uint64_t) l != cluster_size)
142 return -EIO5;
143
144 return 0;
145}
146
147static int normalize_offset(
148 const Header *header,
149 uint64_t p,
150 uint64_t *ret,
151 bool_Bool *compressed,
152 uint64_t *compressed_size) {
153
154 uint64_t q;
155
156 q = be64toh(p);
157
158 if (q & QCOW2_COMPRESSED(1ULL << 62)) {
159 uint64_t sz, csize_shift, csize_mask;
160
161 if (!compressed)
162 return -EOPNOTSUPP95;
163
164 csize_shift = 64 - 2 - (HEADER_CLUSTER_BITS(header)be32toh((header)->cluster_bits) - 8);
165 csize_mask = (1ULL << (HEADER_CLUSTER_BITS(header)be32toh((header)->cluster_bits) - 8)) - 1;
166 sz = (((q >> csize_shift) & csize_mask) + 1) * 512 - (q & 511);
167 q &= ((1ULL << csize_shift) - 1);
168
169 if (compressed_size)
170 *compressed_size = sz;
171
172 *compressed = true1;
173
174 } else {
175 if (compressed) {
176 *compressed = false0;
177 *compressed_size = 0;
178 }
179
180 if (q & QCOW2_ZERO(1ULL << 0)) {
181 /* We make no distinction between zero blocks and holes */
182 *ret = 0;
183 return 0;
184 }
185
186 q &= ~QCOW2_COPIED(1ULL << 63);
187 }
188
189 *ret = q;
190 return q > 0; /* returns positive if not a hole */
191}
192
193static int verify_header(const Header *header) {
194 assert(header)do { if ((__builtin_expect(!!(!(header)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("header"), "../src/import/qcow2-util.c",
194, __PRETTY_FUNCTION__); } while (0)
;
195
196 if (HEADER_MAGIC(header)be32toh((header)->magic) != QCOW2_MAGIC0x514649fb)
197 return -EBADMSG74;
198
199 if (!IN_SET(HEADER_VERSION(header), 2, 3)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){2, 3})/sizeof(int)]; switch(be32toh((header
)->version)) { case 2: case 3: _found = 1; break; default:
break; } _found; })
)
200 return -EOPNOTSUPP95;
201
202 if (HEADER_CRYPT_METHOD(header)be32toh((header)->crypt_method) != 0)
203 return -EOPNOTSUPP95;
204
205 if (HEADER_CLUSTER_BITS(header)be32toh((header)->cluster_bits) < 9) /* 512K */
206 return -EBADMSG74;
207
208 if (HEADER_CLUSTER_BITS(header)be32toh((header)->cluster_bits) > 21) /* 2MB */
209 return -EBADMSG74;
210
211 if (HEADER_SIZE(header)be64toh((header)->size) % HEADER_CLUSTER_SIZE(header)(1ULL << be32toh((header)->cluster_bits)) != 0)
212 return -EBADMSG74;
213
214 if (HEADER_L1_SIZE(header)be32toh((header)->l1_size) > 32*1024*1024) /* 32MB */
215 return -EBADMSG74;
216
217 if (HEADER_VERSION(header)be32toh((header)->version) == 3) {
218
219 if (header->incompatible_features != 0)
220 return -EOPNOTSUPP95;
221
222 if (HEADER_HEADER_LENGTH(header) < sizeof(Header))
223 return -EBADMSG74;
224 }
225
226 return 0;
227}
228
229int qcow2_convert(int qcow2_fd, int raw_fd) {
230 _cleanup_free___attribute__((cleanup(freep))) void *buffer1 = NULL((void*)0), *buffer2 = NULL((void*)0);
231 _cleanup_free___attribute__((cleanup(freep))) be64_t *l1_table = NULL((void*)0), *l2_table = NULL((void*)0);
232 uint64_t sz, i;
233 Header header;
234 ssize_t l;
235 int r;
236
237 l = pread(qcow2_fd, &header, sizeof(header), 0);
238 if (l < 0)
1
Assuming 'l' is >= 0
2
Taking false branch
239 return -errno(*__errno_location ());
240 if (l != sizeof(header))
3
Assuming the condition is false
4
Taking false branch
241 return -EIO5;
242
243 r = verify_header(&header);
244 if (r
4.1
'r' is >= 0
< 0)
5
Taking false branch
245 return r;
246
247 l1_table = new(be64_t, HEADER_L1_SIZE(&header))((be64_t*) malloc_multiply(sizeof(be64_t), (be32toh((&header
)->l1_size))))
;
248 if (!l1_table)
6
Assuming 'l1_table' is non-null
7
Taking false branch
249 return -ENOMEM12;
250
251 l2_table = malloc(HEADER_CLUSTER_SIZE(&header)(1ULL << be32toh((&header)->cluster_bits)));
252 if (!l2_table)
8
Assuming 'l2_table' is non-null
9
Taking false branch
253 return -ENOMEM12;
254
255 buffer1 = malloc(HEADER_CLUSTER_SIZE(&header)(1ULL << be32toh((&header)->cluster_bits)));
256 if (!buffer1)
10
Assuming 'buffer1' is non-null
11
Taking false branch
257 return -ENOMEM12;
258
259 buffer2 = malloc(HEADER_CLUSTER_SIZE(&header)(1ULL << be32toh((&header)->cluster_bits)));
12
Memory is allocated
260 if (!buffer2)
13
Assuming 'buffer2' is non-null
14
Taking false branch
261 return -ENOMEM12;
262
263 /* Empty the file if it exists, we rely on zero bits */
264 if (ftruncate(raw_fd, 0) < 0)
15
Assuming the condition is true
16
Taking true branch
265 return -errno(*__errno_location ());
17
Potential leak of memory pointed to by 'buffer2'
266
267 if (ftruncate(raw_fd, HEADER_SIZE(&header)be64toh((&header)->size)) < 0)
268 return -errno(*__errno_location ());
269
270 sz = sizeof(uint64_t) * HEADER_L1_SIZE(&header)be32toh((&header)->l1_size);
271 l = pread(qcow2_fd, l1_table, sz, HEADER_L1_TABLE_OFFSET(&header)be64toh((&header)->l1_table_offset));
272 if (l < 0)
273 return -errno(*__errno_location ());
274 if ((uint64_t) l != sz)
275 return -EIO5;
276
277 for (i = 0; i < HEADER_L1_SIZE(&header)be32toh((&header)->l1_size); i ++) {
278 uint64_t l2_begin, j;
279
280 r = normalize_offset(&header, l1_table[i], &l2_begin, NULL((void*)0), NULL((void*)0));
281 if (r < 0)
282 return r;
283 if (r == 0)
284 continue;
285
286 l = pread(qcow2_fd, l2_table, HEADER_CLUSTER_SIZE(&header)(1ULL << be32toh((&header)->cluster_bits)), l2_begin);
287 if (l < 0)
288 return -errno(*__errno_location ());
289 if ((uint64_t) l != HEADER_CLUSTER_SIZE(&header)(1ULL << be32toh((&header)->cluster_bits)))
290 return -EIO5;
291
292 for (j = 0; j < HEADER_L2_SIZE(&header)((1ULL << be32toh((&header)->cluster_bits))/sizeof
(uint64_t))
; j++) {
293 uint64_t data_begin, p, compressed_size;
294 bool_Bool compressed;
295
296 p = ((i << HEADER_L2_BITS(&header)(be32toh((&header)->cluster_bits) - 3)) + j) << HEADER_CLUSTER_BITS(&header)be32toh((&header)->cluster_bits);
297
298 r = normalize_offset(&header, l2_table[j], &data_begin, &compressed, &compressed_size);
299 if (r < 0)
300 return r;
301 if (r == 0)
302 continue;
303
304 if (compressed)
305 r = decompress_cluster(
306 qcow2_fd, data_begin,
307 raw_fd, p,
308 compressed_size, HEADER_CLUSTER_SIZE(&header)(1ULL << be32toh((&header)->cluster_bits)),
309 buffer1, buffer2);
310 else
311 r = copy_cluster(
312 qcow2_fd, data_begin,
313 raw_fd, p,
314 HEADER_CLUSTER_SIZE(&header)(1ULL << be32toh((&header)->cluster_bits)), buffer1);
315 if (r < 0)
316 return r;
317 }
318 }
319
320 return 0;
321}
322
323int qcow2_detect(int fd) {
324 be32_t id;
325 ssize_t l;
326
327 l = pread(fd, &id, sizeof(id), 0);
328 if (l < 0)
329 return -errno(*__errno_location ());
330 if (l != sizeof(id))
331 return -EIO5;
332
333 return htobe32(QCOW2_MAGIC0x514649fb) == id;
334}