GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ssh/lib/../sshbuf.c Lines: 0 170 0.0 %
Date: 2017-11-13 Branches: 0 154 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: sshbuf.c,v 1.11 2017/06/01 06:58:25 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
static inline int
30
sshbuf_check_sanity(const struct sshbuf *buf)
31
{
32
	SSHBUF_TELL("sanity");
33
	if (__predict_false(buf == NULL ||
34
	    (!buf->readonly && buf->d != buf->cd) ||
35
	    buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX ||
36
	    buf->cd == NULL ||
37
	    (buf->dont_free && (buf->readonly || buf->parent != NULL)) ||
38
	    buf->max_size > SSHBUF_SIZE_MAX ||
39
	    buf->alloc > buf->max_size ||
40
	    buf->size > buf->alloc ||
41
	    buf->off > buf->size)) {
42
		/* Do not try to recover from corrupted buffer internals */
43
		SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR"));
44
		signal(SIGSEGV, SIG_DFL);
45
		raise(SIGSEGV);
46
		return SSH_ERR_INTERNAL_ERROR;
47
	}
48
	return 0;
49
}
50
51
static void
52
sshbuf_maybe_pack(struct sshbuf *buf, int force)
53
{
54
	SSHBUF_DBG(("force %d", force));
55
	SSHBUF_TELL("pre-pack");
56
	if (buf->off == 0 || buf->readonly || buf->refcount > 1)
57
		return;
58
	if (force ||
59
	    (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) {
60
		memmove(buf->d, buf->d + buf->off, buf->size - buf->off);
61
		buf->size -= buf->off;
62
		buf->off = 0;
63
		SSHBUF_TELL("packed");
64
	}
65
}
66
67
struct sshbuf *
68
sshbuf_new(void)
69
{
70
	struct sshbuf *ret;
71
72
	if ((ret = calloc(sizeof(*ret), 1)) == NULL)
73
		return NULL;
74
	ret->alloc = SSHBUF_SIZE_INIT;
75
	ret->max_size = SSHBUF_SIZE_MAX;
76
	ret->readonly = 0;
77
	ret->refcount = 1;
78
	ret->parent = NULL;
79
	if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) {
80
		free(ret);
81
		return NULL;
82
	}
83
	return ret;
84
}
85
86
struct sshbuf *
87
sshbuf_from(const void *blob, size_t len)
88
{
89
	struct sshbuf *ret;
90
91
	if (blob == NULL || len > SSHBUF_SIZE_MAX ||
92
	    (ret = calloc(sizeof(*ret), 1)) == NULL)
93
		return NULL;
94
	ret->alloc = ret->size = ret->max_size = len;
95
	ret->readonly = 1;
96
	ret->refcount = 1;
97
	ret->parent = NULL;
98
	ret->cd = blob;
99
	ret->d = NULL;
100
	return ret;
101
}
102
103
int
104
sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent)
105
{
106
	int r;
107
108
	if ((r = sshbuf_check_sanity(child)) != 0 ||
109
	    (r = sshbuf_check_sanity(parent)) != 0)
110
		return r;
111
	child->parent = parent;
112
	child->parent->refcount++;
113
	return 0;
114
}
115
116
struct sshbuf *
117
sshbuf_fromb(struct sshbuf *buf)
118
{
119
	struct sshbuf *ret;
120
121
	if (sshbuf_check_sanity(buf) != 0)
122
		return NULL;
123
	if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL)
124
		return NULL;
125
	if (sshbuf_set_parent(ret, buf) != 0) {
126
		sshbuf_free(ret);
127
		return NULL;
128
	}
129
	return ret;
130
}
131
132
void
133
sshbuf_init(struct sshbuf *ret)
134
{
135
	explicit_bzero(ret, sizeof(*ret));
136
	ret->alloc = SSHBUF_SIZE_INIT;
137
	ret->max_size = SSHBUF_SIZE_MAX;
138
	ret->readonly = 0;
139
	ret->dont_free = 1;
140
	ret->refcount = 1;
141
	if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL)
142
		ret->alloc = 0;
143
}
144
145
void
146
sshbuf_free(struct sshbuf *buf)
147
{
148
	int dont_free = 0;
149
150
	if (buf == NULL)
151
		return;
152
	/*
153
	 * The following will leak on insane buffers, but this is the safest
154
	 * course of action - an invalid pointer or already-freed pointer may
155
	 * have been passed to us and continuing to scribble over memory would
156
	 * be bad.
157
	 */
158
	if (sshbuf_check_sanity(buf) != 0)
159
		return;
160
	/*
161
	 * If we are a child, the free our parent to decrement its reference
162
	 * count and possibly free it.
163
	 */
164
	sshbuf_free(buf->parent);
165
	buf->parent = NULL;
166
	/*
167
	 * If we are a parent with still-extant children, then don't free just
168
	 * yet. The last child's call to sshbuf_free should decrement our
169
	 * refcount to 0 and trigger the actual free.
170
	 */
171
	buf->refcount--;
172
	if (buf->refcount > 0)
173
		return;
174
	dont_free = buf->dont_free;
175
	if (!buf->readonly) {
176
		explicit_bzero(buf->d, buf->alloc);
177
		free(buf->d);
178
	}
179
	explicit_bzero(buf, sizeof(*buf));
180
	if (!dont_free)
181
		free(buf);
182
}
183
184
void
185
sshbuf_reset(struct sshbuf *buf)
186
{
187
	u_char *d;
188
189
	if (buf->readonly || buf->refcount > 1) {
190
		/* Nonsensical. Just make buffer appear empty */
191
		buf->off = buf->size;
192
		return;
193
	}
194
	(void) sshbuf_check_sanity(buf);
195
	buf->off = buf->size = 0;
196
	if (buf->alloc != SSHBUF_SIZE_INIT) {
197
		if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT,
198
		    1)) != NULL) {
199
			buf->cd = buf->d = d;
200
			buf->alloc = SSHBUF_SIZE_INIT;
201
		}
202
	}
203
	explicit_bzero(buf->d, SSHBUF_SIZE_INIT);
204
}
205
206
size_t
207
sshbuf_max_size(const struct sshbuf *buf)
208
{
209
	return buf->max_size;
210
}
211
212
size_t
213
sshbuf_alloc(const struct sshbuf *buf)
214
{
215
	return buf->alloc;
216
}
217
218
const struct sshbuf *
219
sshbuf_parent(const struct sshbuf *buf)
220
{
221
	return buf->parent;
222
}
223
224
u_int
225
sshbuf_refcount(const struct sshbuf *buf)
226
{
227
	return buf->refcount;
228
}
229
230
int
231
sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
232
{
233
	size_t rlen;
234
	u_char *dp;
235
	int r;
236
237
	SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size));
238
	if ((r = sshbuf_check_sanity(buf)) != 0)
239
		return r;
240
	if (max_size == buf->max_size)
241
		return 0;
242
	if (buf->readonly || buf->refcount > 1)
243
		return SSH_ERR_BUFFER_READ_ONLY;
244
	if (max_size > SSHBUF_SIZE_MAX)
245
		return SSH_ERR_NO_BUFFER_SPACE;
246
	/* pack and realloc if necessary */
247
	sshbuf_maybe_pack(buf, max_size < buf->size);
248
	if (max_size < buf->alloc && max_size > buf->size) {
249
		if (buf->size < SSHBUF_SIZE_INIT)
250
			rlen = SSHBUF_SIZE_INIT;
251
		else
252
			rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC);
253
		if (rlen > max_size)
254
			rlen = max_size;
255
		SSHBUF_DBG(("new alloc = %zu", rlen));
256
		if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL)
257
			return SSH_ERR_ALLOC_FAIL;
258
		buf->cd = buf->d = dp;
259
		buf->alloc = rlen;
260
	}
261
	SSHBUF_TELL("new-max");
262
	if (max_size < buf->alloc)
263
		return SSH_ERR_NO_BUFFER_SPACE;
264
	buf->max_size = max_size;
265
	return 0;
266
}
267
268
size_t
269
sshbuf_len(const struct sshbuf *buf)
270
{
271
	if (sshbuf_check_sanity(buf) != 0)
272
		return 0;
273
	return buf->size - buf->off;
274
}
275
276
size_t
277
sshbuf_avail(const struct sshbuf *buf)
278
{
279
	if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
280
		return 0;
281
	return buf->max_size - (buf->size - buf->off);
282
}
283
284
const u_char *
285
sshbuf_ptr(const struct sshbuf *buf)
286
{
287
	if (sshbuf_check_sanity(buf) != 0)
288
		return NULL;
289
	return buf->cd + buf->off;
290
}
291
292
u_char *
293
sshbuf_mutable_ptr(const struct sshbuf *buf)
294
{
295
	if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
296
		return NULL;
297
	return buf->d + buf->off;
298
}
299
300
int
301
sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
302
{
303
	int r;
304
305
	if ((r = sshbuf_check_sanity(buf)) != 0)
306
		return r;
307
	if (buf->readonly || buf->refcount > 1)
308
		return SSH_ERR_BUFFER_READ_ONLY;
309
	SSHBUF_TELL("check");
310
	/* Check that len is reasonable and that max_size + available < len */
311
	if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
312
		return SSH_ERR_NO_BUFFER_SPACE;
313
	return 0;
314
}
315
316
int
317
sshbuf_allocate(struct sshbuf *buf, size_t len)
318
{
319
	size_t rlen, need;
320
	u_char *dp;
321
	int r;
322
323
	SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len));
324
	if ((r = sshbuf_check_reserve(buf, len)) != 0)
325
		return r;
326
	/*
327
	 * If the requested allocation appended would push us past max_size
328
	 * then pack the buffer, zeroing buf->off.
329
	 */
330
	sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
331
	SSHBUF_TELL("allocate");
332
	if (len + buf->size <= buf->alloc)
333
		return 0; /* already have it. */
334
335
	/*
336
	 * Prefer to alloc in SSHBUF_SIZE_INC units, but
337
	 * allocate less if doing so would overflow max_size.
338
	 */
339
	need = len + buf->size - buf->alloc;
340
	rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC);
341
	SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
342
	if (rlen > buf->max_size)
343
		rlen = buf->alloc + need;
344
	SSHBUF_DBG(("adjusted rlen %zu", rlen));
345
	if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) {
346
		SSHBUF_DBG(("realloc fail"));
347
		return SSH_ERR_ALLOC_FAIL;
348
	}
349
	buf->alloc = rlen;
350
	buf->cd = buf->d = dp;
351
	if ((r = sshbuf_check_reserve(buf, len)) < 0) {
352
		/* shouldn't fail */
353
		return r;
354
	}
355
	SSHBUF_TELL("done");
356
	return 0;
357
}
358
359
int
360
sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
361
{
362
	u_char *dp;
363
	int r;
364
365
	if (dpp != NULL)
366
		*dpp = NULL;
367
368
	SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len));
369
	if ((r = sshbuf_allocate(buf, len)) != 0)
370
		return r;
371
372
	dp = buf->d + buf->size;
373
	buf->size += len;
374
	if (dpp != NULL)
375
		*dpp = dp;
376
	return 0;
377
}
378
379
int
380
sshbuf_consume(struct sshbuf *buf, size_t len)
381
{
382
	int r;
383
384
	SSHBUF_DBG(("len = %zu", len));
385
	if ((r = sshbuf_check_sanity(buf)) != 0)
386
		return r;
387
	if (len == 0)
388
		return 0;
389
	if (len > sshbuf_len(buf))
390
		return SSH_ERR_MESSAGE_INCOMPLETE;
391
	buf->off += len;
392
	/* deal with empty buffer */
393
	if (buf->off == buf->size)
394
		buf->off = buf->size = 0;
395
	SSHBUF_TELL("done");
396
	return 0;
397
}
398
399
int
400
sshbuf_consume_end(struct sshbuf *buf, size_t len)
401
{
402
	int r;
403
404
	SSHBUF_DBG(("len = %zu", len));
405
	if ((r = sshbuf_check_sanity(buf)) != 0)
406
		return r;
407
	if (len == 0)
408
		return 0;
409
	if (len > sshbuf_len(buf))
410
		return SSH_ERR_MESSAGE_INCOMPLETE;
411
	buf->size -= len;
412
	SSHBUF_TELL("done");
413
	return 0;
414
}
415