Skip to content

Commit 1dbf453

Browse files
levittepaulidale
authored andcommitted
DESERIALIZER: Make OSSL_DESERIALIZER_from_{bio,fp} use BIO_tell() / BIO_seek()
Depending on the BIO used, using BIO_reset() may lead to "interesting" results. For example, a BIO_f_buffer() on top of another BIO that handles BIO_reset() as a BIO_seek(bio, 0), the deserialization process may find itself with a file that's rewound more than expected. Therefore, OSSL_DESERIALIZER_from_{bio,fp}'s behaviour is changed to rely purely on BIO_tell() / BIO_seek(), and since BIO_s_mem() is used internally, it's changed to handle BIO_tell() and BIO_seek() better. This does currently mean that OSSL_DESERIALIZER can't be easily used with streams that don't support BIO_tell() / BIO_seek(). Fixes openssl#12541 Reviewed-by: Paul Dale <[email protected]> (Merged from openssl#12544)
1 parent 3c033c5 commit 1dbf453

File tree

2 files changed

+35
-10
lines changed

2 files changed

+35
-10
lines changed

crypto/bio/bss_mem.c

+22-3
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ static int mem_buf_free(BIO *a)
176176

177177
/*
178178
* Reallocate memory buffer if read pointer differs
179+
* NOT FOR RDONLY
179180
*/
180181
static int mem_buf_sync(BIO *b)
181182
{
@@ -247,12 +248,18 @@ static long mem_ctrl(BIO *b, int cmd, long num, void *ptr)
247248
long ret = 1;
248249
char **pptr;
249250
BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)b->ptr;
250-
BUF_MEM *bm;
251+
BUF_MEM *bm, *bo; /* bio_mem, bio_other */
252+
long off, remain;
251253

252-
if (b->flags & BIO_FLAGS_MEM_RDONLY)
254+
if (b->flags & BIO_FLAGS_MEM_RDONLY) {
253255
bm = bbm->buf;
254-
else
256+
bo = bbm->readp;
257+
} else {
255258
bm = bbm->readp;
259+
bo = bbm->buf;
260+
}
261+
off = bm->data - bo->data;
262+
remain = bm->length;
256263

257264
switch (cmd) {
258265
case BIO_CTRL_RESET:
@@ -270,6 +277,18 @@ static long mem_ctrl(BIO *b, int cmd, long num, void *ptr)
270277
}
271278
}
272279
break;
280+
case BIO_C_FILE_SEEK:
281+
if (num < 0 || num > off + remain)
282+
return -1; /* Can't see outside of the current buffer */
283+
284+
bm->data = bo->data + num;
285+
bm->length = bo->length - num;
286+
bm->max = bo->max - num;
287+
off = num;
288+
/* FALLTHRU */
289+
case BIO_C_FILE_TELL:
290+
ret = off;
291+
break;
273292
case BIO_CTRL_EOF:
274293
ret = (long)(bm->length == 0);
275294
break;

crypto/serializer/deserializer_lib.c

+13-7
Original file line numberDiff line numberDiff line change
@@ -456,13 +456,19 @@ static int deser_process(const OSSL_PARAM params[], void *arg)
456456
&& !OSSL_DESERIALIZER_is_a(deser, new_deser_inst->input_type))
457457
continue;
458458

459-
if (loc == 0) {
460-
if (BIO_reset(bio) <= 0)
461-
goto end;
462-
} else {
463-
if (BIO_seek(bio, loc) <= 0)
464-
goto end;
465-
}
459+
/*
460+
* Checking the return value of BIO_reset() or BIO_seek() is unsafe.
461+
* Furthermore, BIO_reset() is unsafe to use if the source BIO happens
462+
* to be a BIO_s_mem(), because the earlier BIO_tell() gives us zero
463+
* no matter where we are in the underlying buffer we're reading from.
464+
*
465+
* So, we simply do a BIO_seek(), and use BIO_tell() that we're back
466+
* at the same position. This is a best effort attempt, but BIO_seek()
467+
* and BIO_tell() should come as a pair...
468+
*/
469+
(void)BIO_seek(bio, loc);
470+
if (BIO_tell(bio) != loc)
471+
goto end;
466472

467473
/* Recurse */
468474
new_data.current_deser_inst_index = i;

0 commit comments

Comments
 (0)