1 /* $OpenBSD: sshbuf.c,v 1.11 2017/06/01 06:58:25 djm Exp $ */
3 * Copyright (c) 2011 Damien Miller
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.
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.
18 #define SSHBUF_INTERNAL
21 #include <sys/types.h>
32 sshbuf_check_sanity(const struct sshbuf
*buf
)
34 SSHBUF_TELL("sanity");
35 if (__predict_false(buf
== NULL
||
36 (!buf
->readonly
&& buf
->d
!= buf
->cd
) ||
37 buf
->refcount
< 1 || buf
->refcount
> SSHBUF_REFS_MAX
||
39 (buf
->dont_free
&& (buf
->readonly
|| buf
->parent
!= NULL
)) ||
40 buf
->max_size
> SSHBUF_SIZE_MAX
||
41 buf
->alloc
> buf
->max_size
||
42 buf
->size
> buf
->alloc
||
43 buf
->off
> buf
->size
)) {
44 /* Do not try to recover from corrupted buffer internals */
45 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
46 signal(SIGSEGV
, SIG_DFL
);
48 return SSH_ERR_INTERNAL_ERROR
;
54 sshbuf_maybe_pack(struct sshbuf
*buf
, int force
)
56 SSHBUF_DBG(("force %d", force
));
57 SSHBUF_TELL("pre-pack");
58 if (buf
->off
== 0 || buf
->readonly
|| buf
->refcount
> 1)
61 (buf
->off
>= SSHBUF_PACK_MIN
&& buf
->off
>= buf
->size
/ 2)) {
62 memmove(buf
->d
, buf
->d
+ buf
->off
, buf
->size
- buf
->off
);
63 buf
->size
-= buf
->off
;
65 SSHBUF_TELL("packed");
74 if ((ret
= calloc(sizeof(*ret
), 1)) == NULL
)
76 ret
->alloc
= SSHBUF_SIZE_INIT
;
77 ret
->max_size
= SSHBUF_SIZE_MAX
;
81 if ((ret
->cd
= ret
->d
= calloc(1, ret
->alloc
)) == NULL
) {
89 sshbuf_from(const void *blob
, size_t len
)
93 if (blob
== NULL
|| len
> SSHBUF_SIZE_MAX
||
94 (ret
= calloc(sizeof(*ret
), 1)) == NULL
)
96 ret
->alloc
= ret
->size
= ret
->max_size
= len
;
106 sshbuf_set_parent(struct sshbuf
*child
, struct sshbuf
*parent
)
110 if ((r
= sshbuf_check_sanity(child
)) != 0 ||
111 (r
= sshbuf_check_sanity(parent
)) != 0)
113 child
->parent
= parent
;
114 child
->parent
->refcount
++;
119 sshbuf_fromb(struct sshbuf
*buf
)
123 if (sshbuf_check_sanity(buf
) != 0)
125 if ((ret
= sshbuf_from(sshbuf_ptr(buf
), sshbuf_len(buf
))) == NULL
)
127 if (sshbuf_set_parent(ret
, buf
) != 0) {
135 sshbuf_init(struct sshbuf
*ret
)
137 explicit_bzero(ret
, sizeof(*ret
));
138 ret
->alloc
= SSHBUF_SIZE_INIT
;
139 ret
->max_size
= SSHBUF_SIZE_MAX
;
143 if ((ret
->cd
= ret
->d
= calloc(1, ret
->alloc
)) == NULL
)
148 sshbuf_free(struct sshbuf
*buf
)
155 * The following will leak on insane buffers, but this is the safest
156 * course of action - an invalid pointer or already-freed pointer may
157 * have been passed to us and continuing to scribble over memory would
160 if (sshbuf_check_sanity(buf
) != 0)
163 * If we are a child, the free our parent to decrement its reference
164 * count and possibly free it.
166 sshbuf_free(buf
->parent
);
169 * If we are a parent with still-extant children, then don't free just
170 * yet. The last child's call to sshbuf_free should decrement our
171 * refcount to 0 and trigger the actual free.
174 if (buf
->refcount
> 0)
176 dont_free
= buf
->dont_free
;
177 if (!buf
->readonly
) {
178 explicit_bzero(buf
->d
, buf
->alloc
);
181 explicit_bzero(buf
, sizeof(*buf
));
187 sshbuf_reset(struct sshbuf
*buf
)
191 if (buf
->readonly
|| buf
->refcount
> 1) {
192 /* Nonsensical. Just make buffer appear empty */
193 buf
->off
= buf
->size
;
196 (void) sshbuf_check_sanity(buf
);
197 buf
->off
= buf
->size
= 0;
198 if (buf
->alloc
!= SSHBUF_SIZE_INIT
) {
199 if ((d
= recallocarray(buf
->d
, buf
->alloc
, SSHBUF_SIZE_INIT
,
201 buf
->cd
= buf
->d
= d
;
202 buf
->alloc
= SSHBUF_SIZE_INIT
;
205 explicit_bzero(buf
->d
, SSHBUF_SIZE_INIT
);
209 sshbuf_max_size(const struct sshbuf
*buf
)
211 return buf
->max_size
;
215 sshbuf_alloc(const struct sshbuf
*buf
)
220 const struct sshbuf
*
221 sshbuf_parent(const struct sshbuf
*buf
)
227 sshbuf_refcount(const struct sshbuf
*buf
)
229 return buf
->refcount
;
233 sshbuf_set_max_size(struct sshbuf
*buf
, size_t max_size
)
239 SSHBUF_DBG(("set max buf = %p len = %zu", buf
, max_size
));
240 if ((r
= sshbuf_check_sanity(buf
)) != 0)
242 if (max_size
== buf
->max_size
)
244 if (buf
->readonly
|| buf
->refcount
> 1)
245 return SSH_ERR_BUFFER_READ_ONLY
;
246 if (max_size
> SSHBUF_SIZE_MAX
)
247 return SSH_ERR_NO_BUFFER_SPACE
;
248 /* pack and realloc if necessary */
249 sshbuf_maybe_pack(buf
, max_size
< buf
->size
);
250 if (max_size
< buf
->alloc
&& max_size
> buf
->size
) {
251 if (buf
->size
< SSHBUF_SIZE_INIT
)
252 rlen
= SSHBUF_SIZE_INIT
;
254 rlen
= ROUNDUP(buf
->size
, SSHBUF_SIZE_INC
);
257 SSHBUF_DBG(("new alloc = %zu", rlen
));
258 if ((dp
= recallocarray(buf
->d
, buf
->alloc
, rlen
, 1)) == NULL
)
259 return SSH_ERR_ALLOC_FAIL
;
260 buf
->cd
= buf
->d
= dp
;
263 SSHBUF_TELL("new-max");
264 if (max_size
< buf
->alloc
)
265 return SSH_ERR_NO_BUFFER_SPACE
;
266 buf
->max_size
= max_size
;
271 sshbuf_len(const struct sshbuf
*buf
)
273 if (sshbuf_check_sanity(buf
) != 0)
275 return buf
->size
- buf
->off
;
279 sshbuf_avail(const struct sshbuf
*buf
)
281 if (sshbuf_check_sanity(buf
) != 0 || buf
->readonly
|| buf
->refcount
> 1)
283 return buf
->max_size
- (buf
->size
- buf
->off
);
287 sshbuf_ptr(const struct sshbuf
*buf
)
289 if (sshbuf_check_sanity(buf
) != 0)
291 return buf
->cd
+ buf
->off
;
295 sshbuf_mutable_ptr(const struct sshbuf
*buf
)
297 if (sshbuf_check_sanity(buf
) != 0 || buf
->readonly
|| buf
->refcount
> 1)
299 return buf
->d
+ buf
->off
;
303 sshbuf_check_reserve(const struct sshbuf
*buf
, size_t len
)
307 if ((r
= sshbuf_check_sanity(buf
)) != 0)
309 if (buf
->readonly
|| buf
->refcount
> 1)
310 return SSH_ERR_BUFFER_READ_ONLY
;
311 SSHBUF_TELL("check");
312 /* Check that len is reasonable and that max_size + available < len */
313 if (len
> buf
->max_size
|| buf
->max_size
- len
< buf
->size
- buf
->off
)
314 return SSH_ERR_NO_BUFFER_SPACE
;
319 sshbuf_allocate(struct sshbuf
*buf
, size_t len
)
325 SSHBUF_DBG(("allocate buf = %p len = %zu", buf
, len
));
326 if ((r
= sshbuf_check_reserve(buf
, len
)) != 0)
329 * If the requested allocation appended would push us past max_size
330 * then pack the buffer, zeroing buf->off.
332 sshbuf_maybe_pack(buf
, buf
->size
+ len
> buf
->max_size
);
333 SSHBUF_TELL("allocate");
334 if (len
+ buf
->size
<= buf
->alloc
)
335 return 0; /* already have it. */
338 * Prefer to alloc in SSHBUF_SIZE_INC units, but
339 * allocate less if doing so would overflow max_size.
341 need
= len
+ buf
->size
- buf
->alloc
;
342 rlen
= ROUNDUP(buf
->alloc
+ need
, SSHBUF_SIZE_INC
);
343 SSHBUF_DBG(("need %zu initial rlen %zu", need
, rlen
));
344 if (rlen
> buf
->max_size
)
345 rlen
= buf
->alloc
+ need
;
346 SSHBUF_DBG(("adjusted rlen %zu", rlen
));
347 if ((dp
= recallocarray(buf
->d
, buf
->alloc
, rlen
, 1)) == NULL
) {
348 SSHBUF_DBG(("realloc fail"));
349 return SSH_ERR_ALLOC_FAIL
;
352 buf
->cd
= buf
->d
= dp
;
353 if ((r
= sshbuf_check_reserve(buf
, len
)) < 0) {
362 sshbuf_reserve(struct sshbuf
*buf
, size_t len
, u_char
**dpp
)
370 SSHBUF_DBG(("reserve buf = %p len = %zu", buf
, len
));
371 if ((r
= sshbuf_allocate(buf
, len
)) != 0)
374 dp
= buf
->d
+ buf
->size
;
382 sshbuf_consume(struct sshbuf
*buf
, size_t len
)
386 SSHBUF_DBG(("len = %zu", len
));
387 if ((r
= sshbuf_check_sanity(buf
)) != 0)
391 if (len
> sshbuf_len(buf
))
392 return SSH_ERR_MESSAGE_INCOMPLETE
;
394 /* deal with empty buffer */
395 if (buf
->off
== buf
->size
)
396 buf
->off
= buf
->size
= 0;
402 sshbuf_consume_end(struct sshbuf
*buf
, size_t len
)
406 SSHBUF_DBG(("len = %zu", len
));
407 if ((r
= sshbuf_check_sanity(buf
)) != 0)
411 if (len
> sshbuf_len(buf
))
412 return SSH_ERR_MESSAGE_INCOMPLETE
;