Bug Summary

File:src/usr.bin/ssh/ssh/../sshbuf.c
Warning:line 151, column 3
Potential leak of memory pointed to by 'ret'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name sshbuf.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 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/ssh/ssh/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/usr.bin/ssh/ssh/.. -D WITH_OPENSSL -D WITH_ZLIB -D WITH_DSA -D ENABLE_PKCS11 -D HAVE_DLOPEN -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -Wno-unused-parameter -fdebug-compilation-dir=/usr/src/usr.bin/ssh/ssh/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/ssh/ssh/../sshbuf.c
1/* $OpenBSD: sshbuf.c,v 1.19 2022/12/02 04:40:27 djm Exp $ */
2/*
3 * Copyright (c) 2011 Damien Miller
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/types.h>
19#include <signal.h>
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23
24#include "ssherr.h"
25#define SSHBUF_INTERNAL
26#include "sshbuf.h"
27#include "misc.h"
28
29#ifdef SSHBUF_DEBUG
30# define SSHBUF_TELL(what) do { \
31 printf("%s:%d %s: %s size %zu alloc %zu off %zu max %zu\n", \
32 __FILE__"/usr/src/usr.bin/ssh/ssh/../sshbuf.c", __LINE__32, __func__, what, \
33 buf->size, buf->alloc, buf->off, buf->max_size); \
34 fflush(stdout(&__sF[1])); \
35 } while (0)
36#else
37# define SSHBUF_TELL(what)
38#endif
39
40struct sshbuf {
41 u_char *d; /* Data */
42 const u_char *cd; /* Const data */
43 size_t off; /* First available byte is buf->d + buf->off */
44 size_t size; /* Last byte is buf->d + buf->size - 1 */
45 size_t max_size; /* Maximum size of buffer */
46 size_t alloc; /* Total bytes allocated to buf->d */
47 int readonly; /* Refers to external, const data */
48 u_int refcount; /* Tracks self and number of child buffers */
49 struct sshbuf *parent; /* If child, pointer to parent */
50};
51
52static inline int
53sshbuf_check_sanity(const struct sshbuf *buf)
54{
55 SSHBUF_TELL("sanity");
56 if (__predict_false(buf == NULL ||__builtin_expect(((buf == ((void *)0) || (!buf->readonly &&
buf->d != buf->cd) || buf->refcount < 1 || buf->
refcount > 0x100000 || buf->cd == ((void *)0) || buf->
max_size > 0x8000000 || buf->alloc > buf->max_size
|| buf->size > buf->alloc || buf->off > buf->
size) != 0), 0)
57 (!buf->readonly && buf->d != buf->cd) ||__builtin_expect(((buf == ((void *)0) || (!buf->readonly &&
buf->d != buf->cd) || buf->refcount < 1 || buf->
refcount > 0x100000 || buf->cd == ((void *)0) || buf->
max_size > 0x8000000 || buf->alloc > buf->max_size
|| buf->size > buf->alloc || buf->off > buf->
size) != 0), 0)
58 buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX ||__builtin_expect(((buf == ((void *)0) || (!buf->readonly &&
buf->d != buf->cd) || buf->refcount < 1 || buf->
refcount > 0x100000 || buf->cd == ((void *)0) || buf->
max_size > 0x8000000 || buf->alloc > buf->max_size
|| buf->size > buf->alloc || buf->off > buf->
size) != 0), 0)
59 buf->cd == NULL ||__builtin_expect(((buf == ((void *)0) || (!buf->readonly &&
buf->d != buf->cd) || buf->refcount < 1 || buf->
refcount > 0x100000 || buf->cd == ((void *)0) || buf->
max_size > 0x8000000 || buf->alloc > buf->max_size
|| buf->size > buf->alloc || buf->off > buf->
size) != 0), 0)
60 buf->max_size > SSHBUF_SIZE_MAX ||__builtin_expect(((buf == ((void *)0) || (!buf->readonly &&
buf->d != buf->cd) || buf->refcount < 1 || buf->
refcount > 0x100000 || buf->cd == ((void *)0) || buf->
max_size > 0x8000000 || buf->alloc > buf->max_size
|| buf->size > buf->alloc || buf->off > buf->
size) != 0), 0)
61 buf->alloc > buf->max_size ||__builtin_expect(((buf == ((void *)0) || (!buf->readonly &&
buf->d != buf->cd) || buf->refcount < 1 || buf->
refcount > 0x100000 || buf->cd == ((void *)0) || buf->
max_size > 0x8000000 || buf->alloc > buf->max_size
|| buf->size > buf->alloc || buf->off > buf->
size) != 0), 0)
62 buf->size > buf->alloc ||__builtin_expect(((buf == ((void *)0) || (!buf->readonly &&
buf->d != buf->cd) || buf->refcount < 1 || buf->
refcount > 0x100000 || buf->cd == ((void *)0) || buf->
max_size > 0x8000000 || buf->alloc > buf->max_size
|| buf->size > buf->alloc || buf->off > buf->
size) != 0), 0)
63 buf->off > buf->size)__builtin_expect(((buf == ((void *)0) || (!buf->readonly &&
buf->d != buf->cd) || buf->refcount < 1 || buf->
refcount > 0x100000 || buf->cd == ((void *)0) || buf->
max_size > 0x8000000 || buf->alloc > buf->max_size
|| buf->size > buf->alloc || buf->off > buf->
size) != 0), 0)
) {
64 /* Do not try to recover from corrupted buffer internals */
65 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
66 ssh_signal(SIGSEGV11, SIG_DFL(void (*)(int))0);
67 raise(SIGSEGV11);
68 return SSH_ERR_INTERNAL_ERROR-1;
69 }
70 return 0;
71}
72
73static void
74sshbuf_maybe_pack(struct sshbuf *buf, int force)
75{
76 SSHBUF_DBG(("force %d", force));
77 SSHBUF_TELL("pre-pack");
78 if (buf->off == 0 || buf->readonly || buf->refcount > 1)
79 return;
80 if (force ||
81 (buf->off >= SSHBUF_PACK_MIN8192 && buf->off >= buf->size / 2)) {
82 memmove(buf->d, buf->d + buf->off, buf->size - buf->off);
83 buf->size -= buf->off;
84 buf->off = 0;
85 SSHBUF_TELL("packed");
86 }
87}
88
89struct sshbuf *
90sshbuf_new(void)
91{
92 struct sshbuf *ret;
93
94 if ((ret = calloc(sizeof(*ret), 1)) == NULL((void *)0))
95 return NULL((void *)0);
96 ret->alloc = SSHBUF_SIZE_INIT256;
97 ret->max_size = SSHBUF_SIZE_MAX0x8000000;
98 ret->readonly = 0;
99 ret->refcount = 1;
100 ret->parent = NULL((void *)0);
101 if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL((void *)0)) {
102 free(ret);
103 return NULL((void *)0);
104 }
105 return ret;
106}
107
108struct sshbuf *
109sshbuf_from(const void *blob, size_t len)
110{
111 struct sshbuf *ret;
112
113 if (blob == NULL((void *)0) || len
4.1
'len' is <= SSHBUF_SIZE_MAX
> SSHBUF_SIZE_MAX0x8000000 ||
4
Assuming 'blob' is not equal to NULL
7
Taking false branch
114 (ret = calloc(sizeof(*ret), 1)) == NULL((void *)0))
5
Memory is allocated
6
Assuming the condition is false
115 return NULL((void *)0);
116 ret->alloc = ret->size = ret->max_size = len;
117 ret->readonly = 1;
118 ret->refcount = 1;
119 ret->parent = NULL((void *)0);
120 ret->cd = blob;
121 ret->d = NULL((void *)0);
122 return ret;
123}
124
125int
126sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent)
127{
128 int r;
129
130 if ((r = sshbuf_check_sanity(child)) != 0 ||
131 (r = sshbuf_check_sanity(parent)) != 0)
132 return r;
133 if (child->parent != NULL((void *)0) && child->parent != parent)
134 return SSH_ERR_INTERNAL_ERROR-1;
135 child->parent = parent;
136 child->parent->refcount++;
137 return 0;
138}
139
140struct sshbuf *
141sshbuf_fromb(struct sshbuf *buf)
142{
143 struct sshbuf *ret;
144
145 if (sshbuf_check_sanity(buf) != 0)
1
Assuming the condition is false
2
Taking false branch
146 return NULL((void *)0);
147 if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL((void *)0))
3
Calling 'sshbuf_from'
8
Returned allocated memory
9
Taking false branch
148 return NULL((void *)0);
149 if (sshbuf_set_parent(ret, buf) != 0) {
10
Taking true branch
150 sshbuf_free(ret);
11
Calling 'sshbuf_free'
16
Returning from 'sshbuf_free'
151 return NULL((void *)0);
17
Potential leak of memory pointed to by 'ret'
152 }
153 return ret;
154}
155
156void
157sshbuf_free(struct sshbuf *buf)
158{
159 if (buf
11.1
'buf' is not equal to NULL
== NULL((void *)0))
12
Taking false branch
160 return;
161 /*
162 * The following will leak on insane buffers, but this is the safest
163 * course of action - an invalid pointer or already-freed pointer may
164 * have been passed to us and continuing to scribble over memory would
165 * be bad.
166 */
167 if (sshbuf_check_sanity(buf) != 0)
13
Assuming the condition is true
14
Taking true branch
168 return;
15
Returning without deallocating memory or storing the pointer for later deallocation
169
170 /*
171 * If we are a parent with still-extant children, then don't free just
172 * yet. The last child's call to sshbuf_free should decrement our
173 * refcount to 0 and trigger the actual free.
174 */
175 buf->refcount--;
176 if (buf->refcount > 0)
177 return;
178
179 /*
180 * If we are a child, the free our parent to decrement its reference
181 * count and possibly free it.
182 */
183 sshbuf_free(buf->parent);
184 buf->parent = NULL((void *)0);
185
186 if (!buf->readonly) {
187 explicit_bzero(buf->d, buf->alloc);
188 free(buf->d);
189 }
190 freezero(buf, sizeof(*buf));
191}
192
193void
194sshbuf_reset(struct sshbuf *buf)
195{
196 u_char *d;
197
198 if (buf->readonly || buf->refcount > 1) {
199 /* Nonsensical. Just make buffer appear empty */
200 buf->off = buf->size;
201 return;
202 }
203 if (sshbuf_check_sanity(buf) != 0)
204 return;
205 buf->off = buf->size = 0;
206 if (buf->alloc != SSHBUF_SIZE_INIT256) {
207 if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT256,
208 1)) != NULL((void *)0)) {
209 buf->cd = buf->d = d;
210 buf->alloc = SSHBUF_SIZE_INIT256;
211 }
212 }
213 explicit_bzero(buf->d, buf->alloc);
214}
215
216size_t
217sshbuf_max_size(const struct sshbuf *buf)
218{
219 return buf->max_size;
220}
221
222size_t
223sshbuf_alloc(const struct sshbuf *buf)
224{
225 return buf->alloc;
226}
227
228const struct sshbuf *
229sshbuf_parent(const struct sshbuf *buf)
230{
231 return buf->parent;
232}
233
234u_int
235sshbuf_refcount(const struct sshbuf *buf)
236{
237 return buf->refcount;
238}
239
240int
241sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
242{
243 size_t rlen;
244 u_char *dp;
245 int r;
246
247 SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size));
248 if ((r = sshbuf_check_sanity(buf)) != 0)
249 return r;
250 if (max_size == buf->max_size)
251 return 0;
252 if (buf->readonly || buf->refcount > 1)
253 return SSH_ERR_BUFFER_READ_ONLY-49;
254 if (max_size > SSHBUF_SIZE_MAX0x8000000)
255 return SSH_ERR_NO_BUFFER_SPACE-9;
256 /* pack and realloc if necessary */
257 sshbuf_maybe_pack(buf, max_size < buf->size);
258 if (max_size < buf->alloc && max_size > buf->size) {
259 if (buf->size < SSHBUF_SIZE_INIT256)
260 rlen = SSHBUF_SIZE_INIT256;
261 else
262 rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC)((((buf->size)+((256)-1))/(256))*(256));
263 if (rlen > max_size)
264 rlen = max_size;
265 SSHBUF_DBG(("new alloc = %zu", rlen));
266 if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL((void *)0))
267 return SSH_ERR_ALLOC_FAIL-2;
268 buf->cd = buf->d = dp;
269 buf->alloc = rlen;
270 }
271 SSHBUF_TELL("new-max");
272 if (max_size < buf->alloc)
273 return SSH_ERR_NO_BUFFER_SPACE-9;
274 buf->max_size = max_size;
275 return 0;
276}
277
278size_t
279sshbuf_len(const struct sshbuf *buf)
280{
281 if (sshbuf_check_sanity(buf) != 0)
282 return 0;
283 return buf->size - buf->off;
284}
285
286size_t
287sshbuf_avail(const struct sshbuf *buf)
288{
289 if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
290 return 0;
291 return buf->max_size - (buf->size - buf->off);
292}
293
294const u_char *
295sshbuf_ptr(const struct sshbuf *buf)
296{
297 if (sshbuf_check_sanity(buf) != 0)
298 return NULL((void *)0);
299 return buf->cd + buf->off;
300}
301
302u_char *
303sshbuf_mutable_ptr(const struct sshbuf *buf)
304{
305 if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
306 return NULL((void *)0);
307 return buf->d + buf->off;
308}
309
310int
311sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
312{
313 int r;
314
315 if ((r = sshbuf_check_sanity(buf)) != 0)
316 return r;
317 if (buf->readonly || buf->refcount > 1)
318 return SSH_ERR_BUFFER_READ_ONLY-49;
319 SSHBUF_TELL("check");
320 /* Check that len is reasonable and that max_size + available < len */
321 if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
322 return SSH_ERR_NO_BUFFER_SPACE-9;
323 return 0;
324}
325
326int
327sshbuf_allocate(struct sshbuf *buf, size_t len)
328{
329 size_t rlen, need;
330 u_char *dp;
331 int r;
332
333 SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len));
334 if ((r = sshbuf_check_reserve(buf, len)) != 0)
335 return r;
336 /*
337 * If the requested allocation appended would push us past max_size
338 * then pack the buffer, zeroing buf->off.
339 */
340 sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
341 SSHBUF_TELL("allocate");
342 if (len + buf->size <= buf->alloc)
343 return 0; /* already have it. */
344
345 /*
346 * Prefer to alloc in SSHBUF_SIZE_INC units, but
347 * allocate less if doing so would overflow max_size.
348 */
349 need = len + buf->size - buf->alloc;
350 rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC)((((buf->alloc + need)+((256)-1))/(256))*(256));
351 SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
352 if (rlen > buf->max_size)
353 rlen = buf->alloc + need;
354 SSHBUF_DBG(("adjusted rlen %zu", rlen));
355 if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL((void *)0)) {
356 SSHBUF_DBG(("realloc fail"));
357 return SSH_ERR_ALLOC_FAIL-2;
358 }
359 buf->alloc = rlen;
360 buf->cd = buf->d = dp;
361 if ((r = sshbuf_check_reserve(buf, len)) < 0) {
362 /* shouldn't fail */
363 return r;
364 }
365 SSHBUF_TELL("done");
366 return 0;
367}
368
369int
370sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
371{
372 u_char *dp;
373 int r;
374
375 if (dpp != NULL((void *)0))
376 *dpp = NULL((void *)0);
377
378 SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
379 if ((r = sshbuf_allocate(buf, len)) != 0)
380 return r;
381
382 dp = buf->d + buf->size;
383 buf->size += len;
384 if (dpp != NULL((void *)0))
385 *dpp = dp;
386 return 0;
387}
388
389int
390sshbuf_consume(struct sshbuf *buf, size_t len)
391{
392 int r;
393
394 SSHBUF_DBG(("len = %zu", len));
395 if ((r = sshbuf_check_sanity(buf)) != 0)
396 return r;
397 if (len == 0)
398 return 0;
399 if (len > sshbuf_len(buf))
400 return SSH_ERR_MESSAGE_INCOMPLETE-3;
401 buf->off += len;
402 /* deal with empty buffer */
403 if (buf->off == buf->size)
404 buf->off = buf->size = 0;
405 SSHBUF_TELL("done");
406 return 0;
407}
408
409int
410sshbuf_consume_end(struct sshbuf *buf, size_t len)
411{
412 int r;
413
414 SSHBUF_DBG(("len = %zu", len));
415 if ((r = sshbuf_check_sanity(buf)) != 0)
416 return r;
417 if (len == 0)
418 return 0;
419 if (len > sshbuf_len(buf))
420 return SSH_ERR_MESSAGE_INCOMPLETE-3;
421 buf->size -= len;
422 SSHBUF_TELL("done");
423 return 0;
424}
425