Coverage Report

Created: 2026-08-31 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl/crypto/bio/bf_buff.c
Line
Count
Source
1
/*
2
 * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include <stdio.h>
11
#include <errno.h>
12
#include "bio_local.h"
13
#include "internal/cryptlib.h"
14
15
static int buffer_write(BIO *h, const char *buf, int num);
16
static int buffer_read(BIO *h, char *buf, int size);
17
static int buffer_puts(BIO *h, const char *str);
18
static int buffer_gets(BIO *h, char *str, int size);
19
static long buffer_ctrl(BIO *h, int cmd, long arg1, void *arg2);
20
static int buffer_new(BIO *h);
21
static int buffer_free(BIO *data);
22
static long buffer_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp);
23
static int buffer_sendmmsg(BIO *b, BIO_MSG *msg, size_t stride,
24
    size_t num_msg, uint64_t flags,
25
    size_t *msgs_processed);
26
static int buffer_send_next(BIO *b, BIO_F_BUFFER_CTX *ctx,
27
    const char *data, int len);
28
29
0
#define DEFAULT_BUFFER_SIZE 4096
30
31
static const BIO_METHOD methods_buffer = {
32
    BIO_TYPE_BUFFER,
33
    "buffer",
34
    bwrite_conv,
35
    buffer_write,
36
    bread_conv,
37
    buffer_read,
38
    buffer_puts,
39
    buffer_gets,
40
    buffer_ctrl,
41
    buffer_new,
42
    buffer_free,
43
    buffer_callback_ctrl,
44
    buffer_sendmmsg,
45
};
46
47
const BIO_METHOD *BIO_f_buffer(void)
48
0
{
49
0
    return &methods_buffer;
50
0
}
51
52
static int buffer_new(BIO *bi)
53
0
{
54
0
    BIO_F_BUFFER_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
55
56
0
    if (ctx == NULL)
57
0
        return 0;
58
0
    ctx->ibuf_size = DEFAULT_BUFFER_SIZE;
59
0
    ctx->ibuf = OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
60
0
    if (ctx->ibuf == NULL) {
61
0
        OPENSSL_free(ctx);
62
0
        return 0;
63
0
    }
64
0
    ctx->obuf_size = DEFAULT_BUFFER_SIZE;
65
0
    ctx->obuf = OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
66
0
    if (ctx->obuf == NULL) {
67
0
        OPENSSL_free(ctx->ibuf);
68
0
        OPENSSL_free(ctx);
69
0
        return 0;
70
0
    }
71
72
0
    bi->init = 1;
73
0
    bi->ptr = (char *)ctx;
74
0
    bi->flags = 0;
75
0
    return 1;
76
0
}
77
78
static int buffer_free(BIO *a)
79
0
{
80
0
    BIO_F_BUFFER_CTX *b;
81
82
0
    if (a == NULL)
83
0
        return 0;
84
0
    b = (BIO_F_BUFFER_CTX *)a->ptr;
85
0
    OPENSSL_free(b->ibuf);
86
0
    OPENSSL_free(b->obuf);
87
0
#ifndef OPENSSL_NO_SOCK
88
0
    BIO_ADDR_free(b->peer);
89
0
#endif
90
0
    OPENSSL_free(a->ptr);
91
0
    a->ptr = NULL;
92
0
    a->init = 0;
93
0
    a->flags = 0;
94
0
    return 1;
95
0
}
96
97
static int buffer_read(BIO *b, char *out, int outl)
98
0
{
99
0
    int i, num = 0;
100
0
    BIO_F_BUFFER_CTX *ctx;
101
102
0
    if (out == NULL)
103
0
        return 0;
104
0
    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
105
106
0
    if ((ctx == NULL) || (b->next_bio == NULL))
107
0
        return 0;
108
0
    num = 0;
109
0
    BIO_clear_retry_flags(b);
110
111
0
start:
112
0
    i = ctx->ibuf_len;
113
    /* If there is stuff left over, grab it */
114
0
    if (i != 0) {
115
0
        if (i > outl)
116
0
            i = outl;
117
0
        memcpy(out, &(ctx->ibuf[ctx->ibuf_off]), i);
118
0
        ctx->ibuf_off += i;
119
0
        ctx->ibuf_len -= i;
120
0
        num += i;
121
0
        if (outl == i)
122
0
            return num;
123
0
        outl -= i;
124
0
        out += i;
125
0
    }
126
127
    /*
128
     * We may have done a partial read. try to do more. We have nothing in
129
     * the buffer. If we get an error and have read some data, just return it
130
     * and let them retry to get the error again. copy direct to parent
131
     * address space
132
     */
133
0
    if (outl > ctx->ibuf_size) {
134
0
        for (;;) {
135
0
            i = BIO_read(b->next_bio, out, outl);
136
0
            if (i <= 0) {
137
0
                BIO_copy_next_retry(b);
138
0
                if (i < 0)
139
0
                    return ((num > 0) ? num : i);
140
0
                if (i == 0)
141
0
                    return num;
142
0
            }
143
0
            num += i;
144
0
            if (outl == i)
145
0
                return num;
146
0
            out += i;
147
0
            outl -= i;
148
0
        }
149
0
    }
150
    /* else */
151
152
    /* we are going to be doing some buffering */
153
0
    i = BIO_read(b->next_bio, ctx->ibuf, ctx->ibuf_size);
154
0
    if (i <= 0) {
155
0
        BIO_copy_next_retry(b);
156
0
        if (i < 0)
157
0
            return ((num > 0) ? num : i);
158
0
        if (i == 0)
159
0
            return num;
160
0
    }
161
0
    ctx->ibuf_off = 0;
162
0
    ctx->ibuf_len = i;
163
164
    /* Lets re-read using ourselves :-) */
165
0
    goto start;
166
0
}
167
168
static int buffer_write(BIO *b, const char *in, int inl)
169
0
{
170
0
    int i, num = 0;
171
0
    BIO_F_BUFFER_CTX *ctx;
172
173
0
    if ((in == NULL) || (inl <= 0))
174
0
        return 0;
175
0
    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
176
0
    if ((ctx == NULL) || (b->next_bio == NULL))
177
0
        return 0;
178
179
0
    BIO_clear_retry_flags(b);
180
0
start:
181
0
    i = ctx->obuf_size - (ctx->obuf_len + ctx->obuf_off);
182
    /* add to buffer and return */
183
0
    if (i >= inl) {
184
0
        memcpy(&(ctx->obuf[ctx->obuf_off + ctx->obuf_len]), in, inl);
185
0
        ctx->obuf_len += inl;
186
0
        return (num + inl);
187
0
    }
188
    /* else */
189
    /* stuff already in buffer, so add to it first, then flush */
190
0
    if (ctx->obuf_len != 0) {
191
0
        if (i > 0) { /* lets fill it up if we can */
192
0
            memcpy(&(ctx->obuf[ctx->obuf_off + ctx->obuf_len]), in, i);
193
0
            in += i;
194
0
            inl -= i;
195
0
            num += i;
196
0
            ctx->obuf_len += i;
197
0
        }
198
        /* we now have a full buffer needing flushing */
199
0
        for (;;) {
200
0
            i = buffer_send_next(b, ctx, &(ctx->obuf[ctx->obuf_off]),
201
0
                ctx->obuf_len);
202
0
            if (i <= 0) {
203
0
                BIO_copy_next_retry(b);
204
205
0
                if (i < 0)
206
0
                    return ((num > 0) ? num : i);
207
0
                if (i == 0)
208
0
                    return num;
209
0
            }
210
0
            ctx->obuf_off += i;
211
0
            ctx->obuf_len -= i;
212
0
            if (ctx->obuf_len == 0)
213
0
                break;
214
0
        }
215
0
    }
216
    /*
217
     * we only get here if the buffer has been flushed and we still have
218
     * stuff to write
219
     */
220
0
    ctx->obuf_off = 0;
221
222
    /* we now have inl bytes to write */
223
0
    while (inl >= ctx->obuf_size) {
224
0
        i = buffer_send_next(b, ctx, in, inl);
225
0
        if (i <= 0) {
226
0
            BIO_copy_next_retry(b);
227
0
            if (i < 0)
228
0
                return ((num > 0) ? num : i);
229
0
            if (i == 0)
230
0
                return num;
231
0
        }
232
0
        num += i;
233
0
        in += i;
234
0
        inl -= i;
235
0
        if (inl == 0)
236
0
            return num;
237
0
    }
238
239
    /*
240
     * copy the rest into the buffer since we have only a small amount left
241
     */
242
0
    goto start;
243
0
}
244
245
static long buffer_ctrl(BIO *b, int cmd, long num, void *ptr)
246
0
{
247
0
    BIO *dbio;
248
0
    BIO_F_BUFFER_CTX *ctx;
249
0
    long ret = 1;
250
0
    char *p1, *p2;
251
0
    int r, i, *ip;
252
0
    int ibs, obs;
253
254
0
    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
255
256
0
    switch (cmd) {
257
0
    case BIO_CTRL_RESET:
258
0
        ctx->ibuf_off = 0;
259
0
        ctx->ibuf_len = 0;
260
0
        ctx->obuf_off = 0;
261
0
        ctx->obuf_len = 0;
262
0
#ifndef OPENSSL_NO_SOCK
263
0
        BIO_ADDR_free(ctx->peer);
264
0
        ctx->peer = NULL;
265
0
#endif
266
0
        if (b->next_bio == NULL)
267
0
            return 0;
268
0
        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
269
0
        break;
270
0
    case BIO_CTRL_EOF:
271
0
        if (ctx->ibuf_len > 0)
272
0
            return 0;
273
        /*
274
         * If there is no next BIO, BIO_read() returns 0, which means EOF,
275
         * BIO_eof() should return 1 in this case.
276
         */
277
0
        if (b->next_bio == NULL)
278
0
            return 1;
279
0
        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
280
0
        break;
281
0
    case BIO_CTRL_INFO:
282
0
        ret = (long)ctx->obuf_len;
283
0
        break;
284
0
    case BIO_C_GET_BUFF_NUM_LINES:
285
0
        ret = 0;
286
0
        p1 = ctx->ibuf;
287
0
        for (i = 0; i < ctx->ibuf_len; i++) {
288
0
            if (p1[ctx->ibuf_off + i] == '\n')
289
0
                ret++;
290
0
        }
291
0
        break;
292
0
    case BIO_CTRL_WPENDING:
293
0
        ret = (long)ctx->obuf_len;
294
0
        if (ret == 0) {
295
0
            if (b->next_bio == NULL)
296
0
                return 0;
297
0
            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
298
0
        }
299
0
        break;
300
0
    case BIO_CTRL_PENDING:
301
0
        ret = (long)ctx->ibuf_len;
302
0
        if (ret == 0) {
303
0
            if (b->next_bio == NULL)
304
0
                return 0;
305
0
            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
306
0
        }
307
0
        break;
308
0
    case BIO_C_SET_BUFF_READ_DATA:
309
0
        if (num > ctx->ibuf_size) {
310
0
            if (num <= 0)
311
0
                return 0;
312
0
            p1 = OPENSSL_malloc((size_t)num);
313
0
            if (p1 == NULL)
314
0
                return 0;
315
0
            OPENSSL_free(ctx->ibuf);
316
0
            ctx->ibuf = p1;
317
0
        }
318
0
        ctx->ibuf_off = 0;
319
0
        ctx->ibuf_len = (int)num;
320
0
        memcpy(ctx->ibuf, ptr, (int)num);
321
0
        ret = 1;
322
0
        break;
323
0
    case BIO_C_SET_BUFF_SIZE:
324
0
        if (ptr != NULL) {
325
0
            ip = (int *)ptr;
326
0
            if (*ip == 0) {
327
0
                ibs = (int)num;
328
0
                obs = ctx->obuf_size;
329
0
            } else { /* if (*ip == 1) */
330
331
0
                ibs = ctx->ibuf_size;
332
0
                obs = (int)num;
333
0
            }
334
0
        } else {
335
0
            ibs = (int)num;
336
0
            obs = (int)num;
337
0
        }
338
0
        p1 = ctx->ibuf;
339
0
        p2 = ctx->obuf;
340
0
        if ((ibs > DEFAULT_BUFFER_SIZE) && (ibs != ctx->ibuf_size)) {
341
0
            if (num <= 0)
342
0
                return 0;
343
0
            p1 = OPENSSL_malloc((size_t)num);
344
0
            if (p1 == NULL)
345
0
                return 0;
346
0
        }
347
0
        if ((obs > DEFAULT_BUFFER_SIZE) && (obs != ctx->obuf_size)) {
348
0
            p2 = OPENSSL_malloc((size_t)num);
349
0
            if (p2 == NULL) {
350
0
                if (p1 != ctx->ibuf)
351
0
                    OPENSSL_free(p1);
352
0
                return 0;
353
0
            }
354
0
        }
355
0
        if (ctx->ibuf != p1) {
356
0
            OPENSSL_free(ctx->ibuf);
357
0
            ctx->ibuf = p1;
358
0
            ctx->ibuf_off = 0;
359
0
            ctx->ibuf_len = 0;
360
0
            ctx->ibuf_size = ibs;
361
0
        }
362
0
        if (ctx->obuf != p2) {
363
0
            OPENSSL_free(ctx->obuf);
364
0
            ctx->obuf = p2;
365
0
            ctx->obuf_off = 0;
366
0
            ctx->obuf_len = 0;
367
0
            ctx->obuf_size = obs;
368
0
#ifndef OPENSSL_NO_SOCK
369
0
            BIO_ADDR_free(ctx->peer);
370
0
            ctx->peer = NULL;
371
0
#endif
372
0
        }
373
0
        break;
374
0
    case BIO_C_DO_STATE_MACHINE:
375
0
        if (b->next_bio == NULL)
376
0
            return 0;
377
0
        BIO_clear_retry_flags(b);
378
0
        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
379
0
        BIO_copy_next_retry(b);
380
0
        break;
381
382
0
    case BIO_CTRL_FLUSH:
383
0
        if (b->next_bio == NULL)
384
0
            return 0;
385
0
        if (ctx->obuf_len <= 0) {
386
0
            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
387
0
            BIO_copy_next_retry(b);
388
0
            break;
389
0
        }
390
391
0
        for (;;) {
392
0
            BIO_clear_retry_flags(b);
393
0
            if (ctx->obuf_len > 0) {
394
0
                r = buffer_send_next(b, ctx, &(ctx->obuf[ctx->obuf_off]),
395
0
                    ctx->obuf_len);
396
0
                BIO_copy_next_retry(b);
397
0
                if (r <= 0)
398
0
                    return (long)r;
399
0
                ctx->obuf_off += r;
400
0
                ctx->obuf_len -= r;
401
0
            } else {
402
0
                ctx->obuf_len = 0;
403
0
                ctx->obuf_off = 0;
404
0
                break;
405
0
            }
406
0
        }
407
0
        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
408
0
        BIO_copy_next_retry(b);
409
0
#ifndef OPENSSL_NO_SOCK
410
0
        BIO_ADDR_free(ctx->peer);
411
0
        ctx->peer = NULL;
412
0
#endif
413
0
        break;
414
0
    case BIO_CTRL_DUP:
415
0
        dbio = (BIO *)ptr;
416
0
        if (BIO_set_read_buffer_size(dbio, ctx->ibuf_size) <= 0 || BIO_set_write_buffer_size(dbio, ctx->obuf_size) <= 0)
417
0
            ret = 0;
418
0
        break;
419
0
    case BIO_CTRL_PEEK:
420
        /* Ensure there's stuff in the input buffer */
421
0
        {
422
0
            char fake_buf[1];
423
0
            (void)buffer_read(b, fake_buf, 0);
424
0
        }
425
0
        if (num > ctx->ibuf_len)
426
0
            num = ctx->ibuf_len;
427
0
        memcpy(ptr, &(ctx->ibuf[ctx->ibuf_off]), num);
428
0
        ret = num;
429
0
        break;
430
0
    default:
431
0
        if (b->next_bio == NULL)
432
0
            return 0;
433
0
        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
434
0
        break;
435
0
    }
436
0
    return ret;
437
0
}
438
439
static long buffer_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
440
0
{
441
0
    if (b->next_bio == NULL)
442
0
        return 0;
443
0
    return BIO_callback_ctrl(b->next_bio, cmd, fp);
444
0
}
445
446
static int buffer_gets(BIO *b, char *buf, int size)
447
0
{
448
0
    BIO_F_BUFFER_CTX *ctx;
449
0
    int num = 0, i, flag;
450
0
    char *p;
451
452
0
    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
453
0
    size--; /* reserve space for a '\0' */
454
0
    BIO_clear_retry_flags(b);
455
456
0
    for (;;) {
457
0
        if (ctx->ibuf_len > 0) {
458
0
            p = &(ctx->ibuf[ctx->ibuf_off]);
459
0
            flag = 0;
460
0
            for (i = 0; (i < ctx->ibuf_len) && (i < size); i++) {
461
0
                *(buf++) = p[i];
462
0
                if (p[i] == '\n') {
463
0
                    flag = 1;
464
0
                    i++;
465
0
                    break;
466
0
                }
467
0
            }
468
0
            num += i;
469
0
            size -= i;
470
0
            ctx->ibuf_len -= i;
471
0
            ctx->ibuf_off += i;
472
0
            if (flag || size == 0) {
473
0
                *buf = '\0';
474
0
                return num;
475
0
            }
476
0
        } else { /* read another chunk */
477
478
0
            i = BIO_read(b->next_bio, ctx->ibuf, ctx->ibuf_size);
479
0
            if (i <= 0) {
480
0
                BIO_copy_next_retry(b);
481
0
                *buf = '\0';
482
0
                if (i < 0)
483
0
                    return ((num > 0) ? num : i);
484
0
                if (i == 0)
485
0
                    return num;
486
0
            }
487
0
            ctx->ibuf_len = i;
488
0
            ctx->ibuf_off = 0;
489
0
        }
490
0
    }
491
0
}
492
493
static int buffer_puts(BIO *b, const char *str)
494
0
{
495
0
    size_t len = strlen(str);
496
497
0
    if (len > INT_MAX)
498
0
        return -1;
499
0
    return buffer_write(b, str, (int)len);
500
0
}
501
502
/*
503
 * buffer_send_next - write one chunk of buffered output to the next BIO.
504
 *
505
 * For listener-created datagram connections a peer address has been recorded and
506
 * such connections share a single network BIO, so the data must be sent with
507
 * BIO_sendmmsg() carrying the explicit peer address rather than BIO_write().
508
 *
509
 * Returns the number of bytes sent - the full len for the datagram case, which
510
 * is all-or-nothing - or 0 / a negative value on a transient or fatal error.
511
 */
512
static int buffer_send_next(BIO *b, BIO_F_BUFFER_CTX *ctx,
513
    const char *data, int len)
514
0
{
515
0
#ifndef OPENSSL_NO_SOCK
516
0
    if (ctx->peer != NULL) {
517
0
        BIO_MSG msg;
518
0
        size_t processed = 0;
519
520
0
        memset(&msg, 0, sizeof(msg));
521
0
        msg.data = (void *)data;
522
0
        msg.data_len = (size_t)len;
523
0
        msg.peer = ctx->peer;
524
525
        /* Datagrams are all-or-nothing: either the whole chunk goes or none. */
526
0
        if (!BIO_sendmmsg(b->next_bio, &msg, sizeof(msg), 1, 0, &processed)
527
0
            || processed != 1)
528
0
            return -1;
529
0
        return len;
530
0
    }
531
0
#endif
532
0
    return BIO_write(b->next_bio, data, len);
533
0
}
534
535
/*
536
 * buffer_sendmmsg - accumulate a message into the buffer BIO.
537
 *
538
 * Listener-created DTLS connections share a single network BIO and so cannot
539
 * use BIO_write(). Instead the record layer sends each record via BIO_sendmmsg(),
540
 * which lands here. We record the peer address and then buffer the data exactly
541
 * like buffer_write() does, so that multiple handshake records accumulate and are
542
 * packed into a single datagram when the state machine flushes.
543
 */
544
static int buffer_sendmmsg(BIO *b, BIO_MSG *msg, size_t stride,
545
    size_t num_msg, uint64_t flags,
546
    size_t *msgs_processed)
547
0
{
548
0
#ifndef OPENSSL_NO_SOCK
549
0
    BIO_F_BUFFER_CTX *ctx;
550
0
    size_t i;
551
552
0
    *msgs_processed = 0;
553
554
0
    if (b == NULL || b->next_bio == NULL)
555
0
        return 0;
556
557
0
    ctx = (BIO_F_BUFFER_CTX *)b->ptr;
558
0
    if (ctx == NULL || msg == NULL || num_msg == 0)
559
0
        return 0;
560
561
    /*
562
     * Record the peer address on the first write so the flush path knows
563
     * where to send the accumulated data. The address is cleared after each
564
     * flush so it can be set again for the next batch.
565
     */
566
0
    if (ctx->peer == NULL) {
567
0
        if (msg->peer == NULL)
568
0
            return 0;
569
0
        ctx->peer = BIO_ADDR_new();
570
0
        if (ctx->peer == NULL)
571
0
            return 0;
572
0
        if (!BIO_ADDR_copy(ctx->peer, msg->peer)) {
573
0
            BIO_ADDR_free(ctx->peer);
574
0
            ctx->peer = NULL;
575
0
            return 0;
576
0
        }
577
0
    }
578
579
0
    for (i = 0; i < num_msg; i++) {
580
0
        BIO_MSG *m = (BIO_MSG *)((char *)msg + i * stride);
581
0
        int ret;
582
583
        /*
584
         * Buffer the message data. buffer_write() takes an int length, so
585
         * guard against an oversized datagram (this mirrors buffer_puts()).
586
         */
587
0
        if (m->data_len > INT_MAX)
588
0
            break;
589
590
0
        ret = buffer_write(b, m->data, (int)m->data_len);
591
0
        if (ret <= 0)
592
0
            break;
593
594
0
        ++(*msgs_processed);
595
0
    }
596
597
0
    return (*msgs_processed > 0) ? 1 : 0;
598
#else
599
    *msgs_processed = 0;
600
    return 0;
601
#endif
602
0
}