Skip to content

Commit be42447

Browse files
igus68jogme
authored andcommitted
Fix the converters between the old and new BIO_read functions to handle
end-of-file state properly. Related to openssl/project#1745 Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> MergeDate: Thu Feb 12 08:34:31 2026 (Merged from #29290)
1 parent 86105fb commit be42447

12 files changed

Lines changed: 376 additions & 38 deletions

File tree

crypto/bio/bio_lib.c

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,14 @@ int BIO_method_type(const BIO *b)
254254
}
255255

256256
/*
257-
* This is essentially the same as BIO_read_ex() except that it allows
258-
* 0 or a negative value to indicate failure (retryable or not) in the return.
259-
* This is for compatibility with the old style BIO_read(), where existing code
260-
* may make assumptions about the return value that it might get.
257+
* Internal BIO read function. Attempts to read dlen bytes from BIO b and
258+
* places them in data. If any bytes were successfully read, then the number
259+
* of bytes read is stored in readbytes.
260+
* For compatibility with the old-style BIO_read() API, the function uses a
261+
* return-value convention where a positive value indicates success,
262+
* 0 indicates end-of-file, and a negative value indicates an error
263+
* (including retryable errors).
264+
* It also returns 0 if dlen==0.
261265
*/
262266
static int bio_read_intern(BIO *b, void *data, size_t dlen, size_t *readbytes)
263267
{
@@ -285,6 +289,13 @@ static int bio_read_intern(BIO *b, void *data, size_t dlen, size_t *readbytes)
285289
if (ret > 0)
286290
b->num_read += (uint64_t)*readbytes;
287291

292+
/*
293+
* If method->bread() returned 0 when dlen>0, it can be either EOF or
294+
* an error, and we should distinguish them
295+
*/
296+
if (ret == 0 && dlen > 0 && BIO_eof(b) != 1)
297+
ret = -1;
298+
288299
if (HAS_CALLBACK(b))
289300
ret = (int)bio_call_callback(b, BIO_CB_READ | BIO_CB_RETURN, data,
290301
dlen, 0, 0L, ret, readbytes);
@@ -303,8 +314,10 @@ int BIO_read(BIO *b, void *data, int dlen)
303314
size_t readbytes;
304315
int ret;
305316

306-
if (dlen < 0)
307-
return 0;
317+
if (dlen < 0) {
318+
ERR_raise(ERR_LIB_BIO, ERR_R_PASSED_INVALID_ARGUMENT);
319+
return -1;
320+
}
308321

309322
ret = bio_read_intern(b, data, (size_t)dlen, &readbytes);
310323

@@ -679,6 +692,13 @@ long BIO_ctrl(BIO *b, int cmd, long larg, void *parg)
679692
return ret;
680693
}
681694

695+
int BIO_eof(BIO *b)
696+
{
697+
if ((b->flags & BIO_FLAGS_AUTO_EOF) != 0)
698+
return 1;
699+
return (int)BIO_ctrl(b, BIO_CTRL_EOF, 0, NULL);
700+
}
701+
682702
long BIO_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
683703
{
684704
long ret;

crypto/bio/bio_meth.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,25 @@ int bread_conv(BIO *bio, char *data, size_t datal, size_t *readbytes)
121121
{
122122
int ret;
123123

124+
if (datal == 0) {
125+
*readbytes = 0;
126+
return 1;
127+
}
128+
124129
if (datal > INT_MAX)
125130
datal = INT_MAX;
126131

127132
ret = bio->method->bread_old(bio, data, (int)datal);
128133

129-
if (ret <= 0) {
134+
bio->flags &= ~BIO_FLAGS_AUTO_EOF;
135+
if (ret == 0) {
136+
if (BIO_ctrl(bio, BIO_CTRL_EOF, 0, NULL) == 0)
137+
bio->flags |= BIO_FLAGS_AUTO_EOF;
138+
*readbytes = 0;
139+
return 0;
140+
}
141+
142+
if (ret < 0) {
130143
*readbytes = 0;
131144
return ret;
132145
}

doc/man3/BIO_meth_new.pod

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,35 @@ BIO_meth_get_write_ex() and BIO_meth_set_write_ex() get and set the function
111111
used for writing arbitrary length data to the BIO respectively. This function
112112
will be called in response to the application calling BIO_write_ex() or
113113
BIO_write(). The parameters for the function have the same meaning as for
114-
BIO_write_ex(). Older code may call BIO_meth_get_write() and
115-
BIO_meth_set_write() instead. Applications should not call both
116-
BIO_meth_set_write_ex() and BIO_meth_set_write() or call BIO_meth_get_write()
114+
BIO_write_ex() and it must return values as described for BIO_write_ex().
115+
116+
Older code may call BIO_meth_get_write() and BIO_meth_set_write() instead
117+
to set an old-style write function. The parameters for the function have the
118+
same meaning as for BIO_write() and it must return values as described for
119+
BIO_write().
120+
121+
Functions set by BIO_meth_set_write_ex() and BIO_meth_set_write() must call
122+
BIO_set_flags() to set the BIO_FLAGS_SHOULD_RETRY flag in relevant situations.
123+
124+
Applications should not call both BIO_meth_set_write_ex() and
125+
BIO_meth_set_write() or call BIO_meth_get_write()
117126
when the function was set with BIO_meth_set_write_ex().
118127

119128
BIO_meth_get_read_ex() and BIO_meth_set_read_ex() get and set the function used
120129
for reading arbitrary length data from the BIO respectively. This function will
121130
be called in response to the application calling BIO_read_ex() or BIO_read().
122-
The parameters for the function have the same meaning as for BIO_read_ex().
123-
Older code may call BIO_meth_get_read() and BIO_meth_set_read() instead.
131+
The parameters for the function have the same meaning as for BIO_read_ex()
132+
and it must return values as described for BIO_read_ex().
133+
The function must handle the end-of-file condition (if applicable) and return 0
134+
in this case.
135+
136+
Older code may call BIO_meth_get_read() and BIO_meth_set_read() instead to
137+
set an old-style read function. The parameters for the function have the same
138+
meaning as for BIO_read() and it must return values as described for BIO_read().
139+
140+
Functions set by BIO_meth_set_read_ex() and BIO_meth_set_read() must call
141+
BIO_set_flags() to set the BIO_FLAGS_SHOULD_RETRY flag in relevant situations.
142+
124143
Applications should not call both BIO_meth_set_read_ex() and BIO_meth_set_read()
125144
or call BIO_meth_get_read() when the function was set with
126145
BIO_meth_set_read_ex().
@@ -141,6 +160,14 @@ processing ctrl messages in the BIO respectively. See the L<BIO_ctrl(3)> page fo
141160
more information. This function will be called in response to the application
142161
calling BIO_ctrl(). The parameters for the function have the same meaning as for
143162
BIO_ctrl().
163+
If the concept of end-of-file is meaningful for a BIO and the read method is
164+
set using BIO_meth_set_read_ex(), the ctrl function must handle the BIO_CTRL_EOF
165+
command and return an appropriate value (1 if EOF has been reached, 0 if not,
166+
or a negative value on failure), at least immediately after a read operation.
167+
If the read method is set using BIO_meth_set_read(), handling of the
168+
BIO_CTRL_EOF command is not mandatory; however, if such handling is implemented,
169+
it must return 1 if the read function returned 0 when attempting to read a
170+
nonzero number of bytes.
144171

145172
BIO_meth_get_create() and BIO_meth_set_create() get and set the function used
146173
for creating a new instance of the BIO respectively. This function will be

doc/man3/BIO_read.pod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ BIO_read_ex() returns 1 if data was successfully read, and 0 otherwise.
6161
BIO_write_ex() returns 1 if no error was encountered writing data, 0 otherwise.
6262
Requesting to write 0 bytes is not considered an error.
6363

64+
BIO_read() returns the number of bytes read on success.
65+
A return value of 0 indicates that end-of-file was reached, or that a read
66+
of zero bytes was requested.
67+
A negative return value indicates an error condition.
68+
6469
BIO_write() returns -2 if the "write" operation is not implemented by the BIO
6570
or -1 on other errors.
6671
Otherwise it returns the number of bytes written.

doc/man3/BIO_set_flags.pod

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,28 @@ Set if the last I/O operation on the B<BIO> should be retried at a later time.
112112
If this bit is not set then the condition is treated as an error.
113113
This flag is normally set by the B<BIO> implementation.
114114

115+
=back
116+
117+
The following flag can be used only with the B<base64 encoding BIO>.
118+
The result of using this flag with other BIOs is unpredictable.
119+
120+
=over 4
121+
115122
=item B<BIO_FLAGS_BASE64_NO_NL>
116123

117124
When set on a base64 filter B<BIO> this flag disables the generation of
118125
newline characters in the encoded output and causes newlines to be ignored
119126
in the input. See also L<BIO_f_base64(3)>.
120127
The flag has no effect on any other built-in B<BIO> types.
121128

129+
=back
130+
131+
The following flags can be used only with the B<memory buffer BIO> and
132+
B<secure memory buffer BIO>. The result of using these flags with other BIOs
133+
is unpredictable.
134+
135+
=over 4
136+
122137
=item B<BIO_FLAGS_MEM_RDONLY>
123138

124139
When set on a memory B<BIO> this flag indicates that the underlying buffer is
@@ -134,12 +149,8 @@ The flag has no effect on any other built-in B<BIO> types.
134149

135150
=item B<BIO_FLAGS_IN_EOF>
136151

137-
This flag may be used by a B<BIO> implementation to indicate that the end
138-
of the input stream has been reached. However, B<BIO> types are not
139-
required to use this flag to signal end-of-file conditions; they may rely
140-
on other mechanisms such as system calls or by querying the next B<BIO> in a
141-
chain. Applications must therefore not test this flag directly to
142-
determine whether EOF has been reached, and must use BIO_eof() instead.
152+
This flag is for internal use only. It should not be used outside BIO
153+
implementations.
143154

144155
=back
145156

include/internal/bio.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ int bread_conv(BIO *bio, char *data, size_t datal, size_t *read);
4444
#define BIO_CTRL_CLEAR_KTLS_TX_CTRL_MSG 75
4545
#define BIO_CTRL_SET_KTLS_TX_ZEROCOPY_SENDFILE 90
4646

47+
/* Internal BIO flags */
48+
49+
#define BIO_FLAGS_AUTO_EOF 0x80
50+
4751
/*
4852
* This is used with memory BIOs:
4953
* BIO_FLAGS_MEM_LEGACY_EOF means legacy behaviour of BIO_eof()

include/openssl/bio.h.in

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,17 @@ extern "C" {
230230
#define BIO_FLAGS_UPLINK 0
231231
#endif
232232

233+
/* the BIO FLAGS values 0x10 to 0x80 are reserved for internal use */
234+
235+
/*
236+
* BIO FLAGS in the range 0x0100..0x8000 are BIO-type specific.
237+
* Their meaning is defined by the particular BIO implementation and
238+
* is not shared across different BIO types. The same bit value may
239+
* have a different meaning or no meaning at all in other BIOs.
240+
* Such flags may be part of the public API or internal to the BIO.
241+
*/
242+
243+
/* This is used with base64 BIO */
233244
#define BIO_FLAGS_BASE64_NO_NL 0x100
234245

235246
/*
@@ -241,8 +252,6 @@ extern "C" {
241252
#define BIO_FLAGS_NONCLEAR_RST 0x400
242253
#define BIO_FLAGS_IN_EOF 0x800
243254

244-
/* the BIO FLAGS values 0x1000 to 0x8000 are reserved for internal KTLS flags */
245-
246255
typedef union bio_addr_st BIO_ADDR;
247256
typedef struct bio_addrinfo_st BIO_ADDRINFO;
248257

@@ -607,7 +616,6 @@ int BIO_read_filename(BIO *b, const char *name);
607616
#define BIO_dup_state(b, ret) BIO_ctrl(b, BIO_CTRL_DUP, 0, (char *)(ret))
608617

609618
#define BIO_reset(b) (int)BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL)
610-
#define BIO_eof(b) (int)BIO_ctrl(b, BIO_CTRL_EOF, 0, NULL)
611619
#define BIO_set_close(b, c) (int)BIO_ctrl(b, BIO_CTRL_SET_CLOSE, (c), NULL)
612620
#define BIO_get_close(b) (int)BIO_ctrl(b, BIO_CTRL_GET_CLOSE, 0, NULL)
613621
#define BIO_pending(b) (int)BIO_ctrl(b, BIO_CTRL_PENDING, 0, NULL)
@@ -733,6 +741,7 @@ __owur int BIO_get_wpoll_descriptor(BIO *b, BIO_POLL_DESCRIPTOR *desc);
733741
int BIO_puts(BIO *bp, const char *buf);
734742
int BIO_indent(BIO *b, int indent, int max);
735743
long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg);
744+
int BIO_eof(BIO *b);
736745
long BIO_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp);
737746
void *BIO_ptr_ctrl(BIO *bp, int cmd, long larg);
738747
long BIO_int_ctrl(BIO *bp, int cmd, long larg, int iarg);

test/bio_callback_test.c

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
#include "testutil.h"
1515

16-
#define MAXCOUNT 5
16+
#define MAXCOUNT 7
1717
static int my_param_count;
1818
static BIO *my_param_b[MAXCOUNT];
1919
static int my_param_oper[MAXCOUNT];
@@ -138,20 +138,20 @@ static int test_bio_callback_ex(void)
138138
my_param_count = 0;
139139
i = BIO_read(bio, buf, sizeof(buf));
140140
if (!TEST_int_eq(i, 0)
141-
|| !TEST_int_eq(my_param_count, 2)
141+
|| !TEST_int_eq(my_param_count, 6)
142142
|| !TEST_ptr_eq(my_param_b[0], bio)
143143
|| !TEST_int_eq(my_param_oper[0], BIO_CB_READ)
144144
|| !TEST_ptr_eq(my_param_argp[0], buf)
145145
|| !TEST_size_t_eq(my_param_len[0], sizeof(buf))
146146
|| !TEST_long_eq(my_param_argl[0], 0L)
147147
|| !TEST_int_eq((int)my_param_ret[0], 1)
148-
|| !TEST_ptr_eq(my_param_b[1], bio)
149-
|| !TEST_int_eq(my_param_oper[1], BIO_CB_READ | BIO_CB_RETURN)
150-
|| !TEST_ptr_eq(my_param_argp[1], buf)
151-
|| !TEST_size_t_eq(my_param_len[1], sizeof(buf))
152-
|| !TEST_long_eq(my_param_argl[1], 0L)
153-
|| !TEST_size_t_eq(my_param_processed[1], 0)
154-
|| !TEST_int_eq((int)my_param_ret[1], 0))
148+
|| !TEST_ptr_eq(my_param_b[5], bio)
149+
|| !TEST_int_eq(my_param_oper[5], BIO_CB_READ | BIO_CB_RETURN)
150+
|| !TEST_ptr_eq(my_param_argp[5], buf)
151+
|| !TEST_size_t_eq(my_param_len[5], sizeof(buf))
152+
|| !TEST_long_eq(my_param_argl[5], 0L)
153+
|| !TEST_size_t_eq(my_param_processed[5], 0)
154+
|| !TEST_int_eq((int)my_param_ret[5], 0))
155155
goto err;
156156

157157
my_param_count = 0;
@@ -291,19 +291,19 @@ static int test_bio_callback(void)
291291
my_param_count = 0;
292292
i = BIO_read(bio, buf, sizeof(buf));
293293
if (!TEST_int_eq(i, 0)
294-
|| !TEST_int_eq(my_param_count, 2)
294+
|| !TEST_int_eq(my_param_count, 6)
295295
|| !TEST_ptr_eq(my_param_b[0], bio)
296296
|| !TEST_int_eq(my_param_oper[0], BIO_CB_READ)
297297
|| !TEST_ptr_eq(my_param_argp[0], buf)
298298
|| !TEST_int_eq(my_param_argi[0], sizeof(buf))
299299
|| !TEST_long_eq(my_param_argl[0], 0L)
300300
|| !TEST_long_eq(my_param_ret[0], 1L)
301-
|| !TEST_ptr_eq(my_param_b[1], bio)
302-
|| !TEST_int_eq(my_param_oper[1], BIO_CB_READ | BIO_CB_RETURN)
303-
|| !TEST_ptr_eq(my_param_argp[1], buf)
304-
|| !TEST_int_eq(my_param_argi[1], sizeof(buf))
305-
|| !TEST_long_eq(my_param_argl[1], 0L)
306-
|| !TEST_long_eq(my_param_ret[1], 0L))
301+
|| !TEST_ptr_eq(my_param_b[5], bio)
302+
|| !TEST_int_eq(my_param_oper[5], BIO_CB_READ | BIO_CB_RETURN)
303+
|| !TEST_ptr_eq(my_param_argp[5], buf)
304+
|| !TEST_int_eq(my_param_argi[5], sizeof(buf))
305+
|| !TEST_long_eq(my_param_argl[5], 0L)
306+
|| !TEST_long_eq(my_param_ret[5], 0L))
307307
goto err;
308308

309309
my_param_count = 0;

0 commit comments

Comments
 (0)