1 /* $OpenBSD: sshbuf.c,v 1.2 2014/06/25 14:16:09 deraadt 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>
22 #include <sys/param.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 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 if (buf
->parent
!= NULL
) {
167 sshbuf_free(buf
->parent
);
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.
176 if (buf
->refcount
> 0)
178 dont_free
= buf
->dont_free
;
179 if (!buf
->readonly
) {
180 bzero(buf
->d
, buf
->alloc
);
183 bzero(buf
, sizeof(*buf
));
189 sshbuf_reset(struct sshbuf
*buf
)
193 if (buf
->readonly
|| buf
->refcount
> 1) {
194 /* Nonsensical. Just make buffer appear empty */
195 buf
->off
= buf
->size
;
198 if (sshbuf_check_sanity(buf
) == 0)
199 bzero(buf
->d
, buf
->alloc
);
200 buf
->off
= buf
->size
= 0;
201 if (buf
->alloc
!= SSHBUF_SIZE_INIT
) {
202 if ((d
= realloc(buf
->d
, SSHBUF_SIZE_INIT
)) != NULL
) {
203 buf
->cd
= buf
->d
= d
;
204 buf
->alloc
= SSHBUF_SIZE_INIT
;
210 sshbuf_max_size(const struct sshbuf
*buf
)
212 return buf
->max_size
;
216 sshbuf_alloc(const struct sshbuf
*buf
)
221 const struct sshbuf
*
222 sshbuf_parent(const struct sshbuf
*buf
)
228 sshbuf_refcount(const struct sshbuf
*buf
)
230 return buf
->refcount
;
234 sshbuf_set_max_size(struct sshbuf
*buf
, size_t max_size
)
240 SSHBUF_DBG(("set max buf = %p len = %zu", buf
, max_size
));
241 if ((r
= sshbuf_check_sanity(buf
)) != 0)
243 if (max_size
== buf
->max_size
)
245 if (buf
->readonly
|| buf
->refcount
> 1)
246 return SSH_ERR_BUFFER_READ_ONLY
;
247 if (max_size
> SSHBUF_SIZE_MAX
)
248 return SSH_ERR_NO_BUFFER_SPACE
;
249 /* pack and realloc if necessary */
250 sshbuf_maybe_pack(buf
, max_size
< buf
->size
);
251 if (max_size
< buf
->alloc
&& max_size
> buf
->size
) {
252 if (buf
->size
< SSHBUF_SIZE_INIT
)
253 rlen
= SSHBUF_SIZE_INIT
;
255 rlen
= roundup(buf
->size
, SSHBUF_SIZE_INC
);
258 bzero(buf
->d
+ buf
->size
, buf
->alloc
- buf
->size
);
259 SSHBUF_DBG(("new alloc = %zu", rlen
));
260 if ((dp
= realloc(buf
->d
, rlen
)) == NULL
)
261 return SSH_ERR_ALLOC_FAIL
;
262 buf
->cd
= buf
->d
= dp
;
265 SSHBUF_TELL("new-max");
266 if (max_size
< buf
->alloc
)
267 return SSH_ERR_NO_BUFFER_SPACE
;
268 buf
->max_size
= max_size
;
273 sshbuf_len(const struct sshbuf
*buf
)
275 if (sshbuf_check_sanity(buf
) != 0)
277 return buf
->size
- buf
->off
;
281 sshbuf_avail(const struct sshbuf
*buf
)
283 if (sshbuf_check_sanity(buf
) != 0 || buf
->readonly
|| buf
->refcount
> 1)
285 return buf
->max_size
- (buf
->size
- buf
->off
);
289 sshbuf_ptr(const struct sshbuf
*buf
)
291 if (sshbuf_check_sanity(buf
) != 0)
293 return buf
->cd
+ buf
->off
;
297 sshbuf_mutable_ptr(const struct sshbuf
*buf
)
299 if (sshbuf_check_sanity(buf
) != 0 || buf
->readonly
|| buf
->refcount
> 1)
301 return buf
->d
+ buf
->off
;
305 sshbuf_check_reserve(const struct sshbuf
*buf
, size_t len
)
309 if ((r
= sshbuf_check_sanity(buf
)) != 0)
311 if (buf
->readonly
|| buf
->refcount
> 1)
312 return SSH_ERR_BUFFER_READ_ONLY
;
313 SSHBUF_TELL("check");
314 /* Check that len is reasonable and that max_size + available < len */
315 if (len
> buf
->max_size
|| buf
->max_size
- len
< buf
->size
- buf
->off
)
316 return SSH_ERR_NO_BUFFER_SPACE
;
321 sshbuf_reserve(struct sshbuf
*buf
, size_t len
, u_char
**dpp
)
330 SSHBUF_DBG(("reserve buf = %p len = %zu", buf
, len
));
331 if ((r
= sshbuf_check_reserve(buf
, len
)) != 0)
334 * If the requested allocation appended would push us past max_size
335 * then pack the buffer, zeroing buf->off.
337 sshbuf_maybe_pack(buf
, buf
->size
+ len
> buf
->max_size
);
338 SSHBUF_TELL("reserve");
339 if (len
+ buf
->size
> buf
->alloc
) {
341 * Prefer to alloc in SSHBUF_SIZE_INC units, but
342 * allocate less if doing so would overflow max_size.
344 need
= len
+ buf
->size
- buf
->alloc
;
345 rlen
= roundup(buf
->alloc
+ need
, SSHBUF_SIZE_INC
);
346 SSHBUF_DBG(("need %zu initial rlen %zu", need
, rlen
));
347 if (rlen
> buf
->max_size
)
348 rlen
= buf
->alloc
+ need
;
349 SSHBUF_DBG(("adjusted rlen %zu", rlen
));
350 if ((dp
= realloc(buf
->d
, rlen
)) == NULL
) {
351 SSHBUF_DBG(("realloc fail"));
354 return SSH_ERR_ALLOC_FAIL
;
357 buf
->cd
= buf
->d
= dp
;
358 if ((r
= sshbuf_check_reserve(buf
, len
)) < 0) {
365 dp
= buf
->d
+ buf
->size
;
374 sshbuf_consume(struct sshbuf
*buf
, size_t len
)
378 SSHBUF_DBG(("len = %zu", len
));
379 if ((r
= sshbuf_check_sanity(buf
)) != 0)
383 if (len
> sshbuf_len(buf
))
384 return SSH_ERR_MESSAGE_INCOMPLETE
;
391 sshbuf_consume_end(struct sshbuf
*buf
, size_t len
)
395 SSHBUF_DBG(("len = %zu", len
));
396 if ((r
= sshbuf_check_sanity(buf
)) != 0)
400 if (len
> sshbuf_len(buf
))
401 return SSH_ERR_MESSAGE_INCOMPLETE
;