Skip to content

Commit 879ba8a

Browse files
committed
Merge branch 'dev_minor' into oss_fuzz_tests
2 parents bef6104 + 33398db commit 879ba8a

File tree

4 files changed

+81
-71
lines changed

4 files changed

+81
-71
lines changed

channels.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,9 +534,16 @@ channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd,
534534
(c->output = sshbuf_new()) == NULL ||
535535
(c->extended = sshbuf_new()) == NULL)
536536
fatal_f("sshbuf_new failed");
537+
538+
/* these buffers are important in terms of tracking channel
539+
* buffer usage so label and type them with descriptive names */
537540
sshbuf_relabel(c->input, "channel input");
541+
sshbuf_type(c->input, BUF_CHANNEL_INPUT);
538542
sshbuf_relabel(c->output, "channel output");
543+
sshbuf_type(c->output, BUF_CHANNEL_OUTPUT);
539544
sshbuf_relabel(c->extended, "channel extended");
545+
sshbuf_type(c->extended, BUF_CHANNEL_EXTENDED);
546+
540547
if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0)
541548
fatal_fr(r, "sshbuf_set_max_size");
542549
c->ostate = CHAN_OUTPUT_OPEN;

packet.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,15 @@ ssh_alloc_session_state(void)
257257
(state->incoming_packet = sshbuf_new()) == NULL)
258258
goto fail;
259259
/* these buffers are important in terms of tracking buffer usage
260-
* so we explicitly label them with descriptive names */
260+
* so we explicitly label and type them with descriptive names */
261261
sshbuf_relabel(state->input, "input");
262+
sshbuf_type(state->input, "BUF_PACKET_INPUT");
262263
sshbuf_relabel(state->incoming_packet, "inpacket");
264+
sshbuf_type(state->incoming_packet, "BUF_PACKET_INCOMING");
263265
sshbuf_relabel(state->output, "output");
266+
sshbuf_type(state->output, "BUF_PACKET_OUTPUT");
264267
sshbuf_relabel(state->outgoing_packet, "outpacket");
268+
sshbuf_type(state->outgoing_packet, "BUF_PACKET_OUTGOING");
265269

266270
TAILQ_INIT(&state->outgoing);
267271
TAILQ_INIT(&ssh->private_keys);

sshbuf.c

Lines changed: 49 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -42,42 +42,37 @@
4242
# define SSHBUF_TELL(what)
4343
#endif
4444

45-
#ifdef SSHBUF_DEBUG
46-
# define SSHBUF_TELL(what) do { \
47-
printf("%s:%d %s: %s size %zu alloc %zu off %zu max %zu\n", \
48-
__FILE__, __LINE__, __func__, what, \
49-
buf->size, buf->alloc, buf->off, buf->max_size); \
50-
fflush(stdout); \
51-
} while (0)
52-
#else
53-
# define SSHBUF_TELL(what)
54-
#endif
55-
5645
struct sshbuf {
5746
u_char *d; /* Data */
5847
const u_char *cd; /* Const data */
5948
size_t off; /* First available byte is buf->d + buf->off */
6049
size_t size; /* Last byte is buf->d + buf->size - 1 */
6150
size_t max_size; /* Maximum size of buffer */
62-
size_t window_max; /* channel window max */
63-
size_t alloc; /* Total bytes allocated to buf->d */
51+
size_t alloc; /* Total bytes allocated to buf->d */
6452
int readonly; /* Refers to external, const data */
6553
u_int refcount; /* Tracks self and number of child buffers */
6654
struct sshbuf *parent; /* If child, pointer to parent */
6755
char label[MAX_LABEL_LEN]; /* String for buffer label - debugging use */
68-
struct timeval buf_ts; /* creation time of buffer */
56+
int type; /* type of buffer enum (sshbuf_types)*/
6957
};
7058

59+
/* update the label string for a given sshbuf. Useful
60+
* for debugging */
7161
void
7262
sshbuf_relabel(struct sshbuf *buf, const char *label)
7363
{
7464
if (label != NULL)
7565
strncpy(buf->label, label, MAX_LABEL_LEN-1);
7666
}
7767

78-
float time_diff(struct timeval *start, struct timeval *end)
68+
/* set the type (from enum sshbuf_type) of the given sshbuf.
69+
* The purpose is to allow different classes of buffers to
70+
* follow different code paths if necessary */
71+
void
72+
sshbuf_type(struct sshbuf *buf, int type)
7973
{
80-
return (end->tv_sec - start->tv_sec) + 1e-6*(end->tv_usec - start->tv_usec);
74+
if (type < BUF_MAX_TYPE)
75+
buf->type = type;
8176
}
8277

8378
static inline int
@@ -249,7 +244,7 @@ sshbuf_reset(struct sshbuf *buf)
249244
size_t
250245
sshbuf_max_size(const struct sshbuf *buf)
251246
{
252-
return buf->max_size / 2;
247+
return buf->max_size;
253248
}
254249

255250
size_t
@@ -271,18 +266,13 @@ sshbuf_refcount(const struct sshbuf *buf)
271266
}
272267

273268
int
274-
sshbuf_set_max_size(struct sshbuf *buf, size_t requested_size)
269+
sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
275270
{
276-
size_t rlen, max_size = requested_size * 2;
271+
size_t rlen;
277272
u_char *dp;
278273
int r;
279274

280-
/*
281-
* Note that we set the actual allocation limit to be 2x the requested
282-
* size. This is to avoid some pathological compaction behaviour later
283-
* when a buffer is at capacity and has small drains/fills on it.
284-
*/
285-
SSHBUF_DBG(("set max buf = %p requested = %zu", buf, requested_size));
275+
SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size));
286276
if ((r = sshbuf_check_sanity(buf)) != 0)
287277
return r;
288278
if (max_size == buf->max_size)
@@ -291,17 +281,9 @@ sshbuf_set_max_size(struct sshbuf *buf, size_t requested_size)
291281
return SSH_ERR_BUFFER_READ_ONLY;
292282
if (max_size > SSHBUF_SIZE_MAX)
293283
return SSH_ERR_NO_BUFFER_SPACE;
294-
/*
295-
* Always pack as it makes everything that follows easier.
296-
* Potentially expensive, but this should seldom be called on buffers
297-
* that already contain data.
298-
*/
299-
sshbuf_maybe_pack(buf, 1);
300-
/* Refuse setting a maximum below current amount of data in buffer */
301-
if (requested_size < buf->size)
302-
return SSH_ERR_NO_BUFFER_SPACE;
303-
/* Shrink alloc if the existing allocation is larger than requested */
304-
if (requested_size < buf->alloc) {
284+
/* pack and realloc if necessary */
285+
sshbuf_maybe_pack(buf, max_size < buf->size);
286+
if (max_size < buf->alloc && max_size > buf->size) {
305287
if (buf->size < SSHBUF_SIZE_INIT)
306288
rlen = SSHBUF_SIZE_INIT;
307289
else
@@ -315,6 +297,8 @@ sshbuf_set_max_size(struct sshbuf *buf, size_t requested_size)
315297
buf->alloc = rlen;
316298
}
317299
SSHBUF_TELL("new-max");
300+
if (max_size < buf->alloc)
301+
return SSH_ERR_NO_BUFFER_SPACE;
318302
buf->max_size = max_size;
319303
return 0;
320304
}
@@ -332,7 +316,18 @@ sshbuf_avail(const struct sshbuf *buf)
332316
{
333317
if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
334318
return 0;
335-
return (buf->max_size / 2) - (buf->size - buf->off);
319+
/* we need to reserve a small amount of overhead on the input buffer
320+
* or we can enter into a pathological state during bulk
321+
* data transfers. We use a fraction of the max size as we want it to scale
322+
* with the size of the input buffer. If we do it for all of the buffers
323+
* we fail the regression unit tests. This seems like a reasonable
324+
* solution. Of course, I still need to figure out *why* this is
325+
* happening and come up with an actual fix. TODO
326+
* cjr 4/19/2024 */
327+
if (buf->type == BUF_CHANNEL_INPUT)
328+
return buf->max_size / 1.05 - (buf->size - buf->off);
329+
else
330+
return buf->max_size - (buf->size - buf->off);
336331
}
337332

338333
const u_char *
@@ -361,19 +356,12 @@ sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
361356
if (buf->readonly || buf->refcount > 1)
362357
return SSH_ERR_BUFFER_READ_ONLY;
363358
SSHBUF_TELL("check");
364-
/* Check that len is reasonable and that max size + available < len */
365-
if (len > (buf->max_size / 2) ||
366-
(buf->max_size / 2) - len < buf->size - buf->off)
359+
/* Check that len is reasonable and that max_size + available < len */
360+
if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
367361
return SSH_ERR_NO_BUFFER_SPACE;
368362
return 0;
369363
}
370364

371-
void
372-
sshbuf_set_window_max(struct sshbuf *buf, size_t len)
373-
{
374-
buf->window_max = len;
375-
}
376-
377365
int
378366
sshbuf_allocate(struct sshbuf *buf, size_t len)
379367
{
@@ -403,38 +391,32 @@ sshbuf_allocate(struct sshbuf *buf, size_t len)
403391
* slowly. It's knows that it needs to grow but it only does so 32K
404392
* at a time. This means a lot of calls to realloc and memcpy which
405393
* kills performance until the buffer reaches some maximum size.
406-
* so we explicitly test for a buffer that's trying to grow and
407-
* if it is then we push the growth to whatever the adjusted value of
408-
* local_window_max happens to be. This significantly reduces overhead
394+
* So we explicitly test for a buffer that's trying to grow and
395+
* if it is then we push the growth by 4MB at a time. This can result in
396+
* the buffer being over allocated (in terms of actual needs) but the
397+
* process is fast. This significantly reduces overhead
409398
* and improves performance. In this case we look for a buffer that is trying
410399
* to grow larger than BUF_WATERSHED (256*1024 taken from PACKET_MAX_SIZE)
411-
* and where the local_window_max isn't zero (which is usally in the Channels
412-
* struct but we copied it into the shhbuf as window_max). If it is zero or
413-
* the buffer is smaller than BUF_WATERSHED we just use the
414-
* normal value for need. We also don't want to grow the buffer past
415-
* what we need (the size of window_max) so if the current allocation (in
416-
* buf->alloc) is greater than window_max we skip it.
417-
*
418-
* Turns out the extra functions on the following conditional aren't needed
419-
* -cjr 04/06/23
400+
* and explcitly check that the buffer is being used for inbound outbound
401+
* channel buffering.
402+
* Updated for 18.4.1 -cjr 04/20/24
420403
*/
421-
if (rlen > BUF_WATERSHED) {
422-
/* debug_f ("Prior: label: %s, %p, rlen is %zu need is %zu win_max is %zu max_size is %zu",
423-
buf->label, buf, rlen, need, buf->window_max, buf->max_size); */
404+
if (rlen > BUF_WATERSHED && (buf->type == BUF_CHANNEL_OUTPUT || buf->type == BUF_CHANNEL_INPUT)) {
405+
/* debug_f ("Prior: label: %s, %p, rlen is %zu need is %zu max_size is %zu",
406+
buf->label, buf, rlen, need, buf->max_size); */
424407
/* easiest thing to do is grow the nuffer by 4MB each time. It might end
425408
* up being somewhat overallocated but works quickly */
426409
need = (4*1024*1024);
427410
rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC);
428-
/* debug_f ("Post: label: %s, %p, rlen is %zu need is %zu win_max is %zu max_size is %zu", */
429-
/* buf->label, buf, rlen, need, buf->window_max, buf->max_size); */
411+
/* debug_f ("Post: label: %s, %p, rlen is %zu need is %zu max_size is %zu", */
412+
/* buf->label, buf, rlen, need, buf->max_size); */
430413
}
431414
SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen));
432415

433416
/* rlen might be above the max allocation */
434-
if (rlen > buf->max_size) {
417+
if (rlen > buf->max_size)
435418
rlen = buf->max_size;
436-
/* debug_f("set rlen to %zu", buf->max_size);*/
437-
}
419+
438420
SSHBUF_DBG(("adjusted rlen %zu", rlen));
439421
if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) {
440422
SSHBUF_DBG(("realloc fail"));
@@ -484,9 +466,8 @@ sshbuf_consume(struct sshbuf *buf, size_t len)
484466
return SSH_ERR_MESSAGE_INCOMPLETE;
485467
buf->off += len;
486468
/* deal with empty buffer */
487-
if (buf->off == buf->size) {
469+
if (buf->off == buf->size)
488470
buf->off = buf->size = 0;
489-
}
490471
SSHBUF_TELL("done");
491472
return 0;
492473
}

sshbuf.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
# endif /* OPENSSL_HAS_ECC */
2929
#endif /* WITH_OPENSSL */
3030

31-
#define SSHBUF_SIZE_MAX 0xFFFFFFF /* Hard maximum size 256MB */
31+
#define SSHBUF_SIZE_MAX 0x8000000 /* Hard maximum size 128MB */
3232
#define SSHBUF_REFS_MAX 0x100000 /* Max child buffers */
3333
#define SSHBUF_MAX_BIGNUM (16384 / 8) /* Max bignum *bytes* */
3434
#define SSHBUF_MAX_ECPOINT ((528 * 2 / 8) + 1) /* Max EC point *bytes* */
@@ -37,11 +37,22 @@
3737

3838
struct sshbuf;
3939

40+
enum buffer_types {
41+
BUF_CHANNEL_OUTPUT,
42+
BUF_CHANNEL_INPUT,
43+
BUF_CHANNEL_EXTENDED,
44+
BUF_PACKET_INPUT,
45+
BUF_PACKET_INCOMING,
46+
BUF_PACKET_OUTPUT,
47+
BUF_PACKET_OUTGOING,
48+
BUF_MAX_TYPE
49+
};
50+
4051
/*
4152
* Create a new sshbuf buffer.
4253
* Returns pointer to buffer on success, or NULL on allocation failure.
4354
*/
44-
/* struct sshbuf *sshbuf_new(void); */
55+
/* struct sshbuf *sshbuf_new(void);*/
4556

4657
/*
4758
* Create a new labeled sshbuf buffer.
@@ -54,6 +65,13 @@ struct sshbuf *sshbuf_new_label(const char *);
5465
*/
5566
void sshbuf_relabel(struct sshbuf *, const char *);
5667

68+
/*
69+
* assign a type (from the buffer_types enum) to
70+
* the buffer. Used to quickly identify the purpose of
71+
* the buffer.
72+
*/
73+
void sshbuf_type(struct sshbuf *, int);
74+
5775
/*
5876
* Create a new, read-only sshbuf buffer from existing data.
5977
* Returns pointer to buffer on success, or NULL on allocation failure.

0 commit comments

Comments
 (0)