Skip to content

Implement legacy callback with BIO_set_callback #2285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Apr 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
46a7c05
Updated parameters for BIOPairTest to cover combinations of callback …
kingstjo Mar 19, 2025
1a30199
Updated TestPutsCallbacks to use BIOPairTest to cover combinations of…
kingstjo Mar 19, 2025
b1065db
Updated Tests to use BIOPairTest to cover combinations of callback an…
kingstjo Mar 19, 2025
37f2f98
updated TestPutsCallback only takes in bool pair for callback type
kingstjo Mar 20, 2025
09d1cfc
Updated comments, grouped callback_ex verifications
kingstjo Mar 20, 2025
64c0088
Added old-style callback, replaced direct calls to callback_ex with g…
kingstjo Mar 20, 2025
f8b098b
Updated comment styling
kingstjo Mar 20, 2025
4133a1f
Merge branch 'aws:main' into implement-callback-dump
kingstjo Mar 20, 2025
505867d
arguments len and processed aren't handled for legacy callback, as we…
kingstjo Mar 21, 2025
0263e2c
fixed signature BIO_callback_fn to properly reflect bio_ret as long
kingstjo Mar 25, 2025
67fb72e
moved call_bio_callback_with_processed logic for len and processed fo…
kingstjo Mar 25, 2025
326c5e2
BIO_write is now using get_callback instead of call_bio_callback_with…
kingstjo Mar 25, 2025
47df038
BIO_read now uses get_callback instead of call_bio_callback_with_proc…
kingstjo Mar 25, 2025
f089882
BIO_gets now uses get_callback instead of call_bio_callback_with_proc…
kingstjo Mar 25, 2025
31ff0fa
With router get_callback additional check for null made macro HAS_CAL…
kingstjo Mar 25, 2025
47d035f
removed usage of macro HAS_LEN_OPER
kingstjo Mar 26, 2025
dff9ceb
Moved redundant callback return logic for operations using processed …
kingstjo Mar 26, 2025
1672053
Added missing check before casting size_t to int
kingstjo Mar 26, 2025
c43447d
fixed indentations
kingstjo Mar 26, 2025
aa851be
moved assignment of return value into processed for read, write, puts…
kingstjo Mar 26, 2025
5914acc
replace checks with assert in internal use function
kingstjo Mar 26, 2025
bb3caff
Updated comments to better reflect hanlding of additional arguments f…
kingstjo Mar 26, 2025
022d32a
Fixed implicit casting long to int
kingstjo Mar 27, 2025
fa95ace
Added comments explaining reset of ret value
kingstjo Mar 27, 2025
845c37f
Fixed spacing and inline declarations
kingstjo Mar 27, 2025
a56f411
properly check long is within INT bounds
kingstjo Mar 27, 2025
b23946d
removed added new lines
kingstjo Mar 27, 2025
9a5f2b1
Updated callback_fn_wrap_ex to not use processed if callback returns 0
kingstjo Mar 27, 2025
18362cd
add defensive assert for callback_fn_wrap_ex ensuring callback_ex is …
kingstjo Mar 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 149 additions & 69 deletions crypto/bio/bio.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,34 +68,114 @@

#include "../internal.h"

#define HAS_CALLBACK(b) ((b)->callback_ex != NULL)

// Helper function to create a placeholder |processed| that the callback can
// modify and return to the caller. Used only in callbacks that pass in
// |processed|.
static int call_bio_callback_with_processed(BIO *bio, const int oper,
const void *buf, int len, int ret) {
if (HAS_CALLBACK(bio)) {
size_t processed = 0;
// The original BIO return value can be an error value (less than 0) or
// the number of bytes read/written
if (ret > 0) {
processed = ret;
// |callback_fn_wrap_ex| adapts the legacy callback interface |BIO_callback_fn| to the
// extended callback interface |BIO_callback_fn_ex|. This function should only be
// called when |callback_ex| is not available and the legacy callback is set.
//
// The extended interface parameters |len| and |processed| are mapped to the legacy
// interface parameters |argi| and |bio_ret| respectively.
//
// Returns -1 on NULL |BIO| or callback, otherwise returns the result of the legacy
// callback.
static long callback_fn_wrap_ex(BIO *bio, int oper, const char *argp,
size_t len, int argi, long argl, int bio_ret,
size_t *processed) {
assert(bio != NULL);
assert(bio->callback != NULL);
assert(bio->callback_ex == NULL);

/* Strip off any BIO_CB_RETURN flag */
int bareoper = oper & ~BIO_CB_RETURN;

if (bareoper == BIO_CB_READ || bareoper == BIO_CB_WRITE
|| bareoper == BIO_CB_GETS) {
/* In this case |len| is set, and should be used instead of |argi| */
if (len > INT_MAX) {
return -1;
}
// Pass the original BIO's return value to the callback. If the callback
// is successful return processed from the callback, if the callback is
// not successful return the callback's return value.
long callback_ret = bio->callback_ex(bio, oper, buf, len, 0, 0L, ret, &processed);
if (callback_ret <= INT_MAX && callback_ret >= INT_MIN) {
ret = (int)callback_ret;
if (ret > 0) {
// BIO will only read int |len| bytes so this is a safe cast
ret = (int)processed;
}

argi = (int)len;
}

if (bio_ret > 0 && (oper & BIO_CB_RETURN) && bareoper != BIO_CB_CTRL) {
if (*processed > INT_MAX) {
return -1;
}

bio_ret = *processed;
}


long ret = bio->callback(bio, oper, argp, argi, argl, bio_ret);

if (ret > 0 && (oper & BIO_CB_RETURN) && bareoper != BIO_CB_CTRL) {
*processed = (size_t)ret;
ret = 1;
}

return ret;
}

// |get_callback| returns the appropriate callback function for a given |BIO|, preferring
// the extended interface |callback_ex| over the legacy interface.
//
// When only the legacy callback is available, it is wrapped in the extended format
// via |callback_fn_wrap_ex| to provide a consistent interface. The extended callback
// provides additional parameters for length and bytes processed tracking.
//
// Returns the |callback_ex| function if available, a wrapped legacy callback if only
// |callback| is set, or NULL if no callbacks are set.
static BIO_callback_fn_ex get_callback(BIO *bio) {
assert(bio != NULL);

if (bio->callback_ex != NULL) {
return bio->callback_ex;
}
if (bio->callback != NULL) {
// Wrap old-style callback in extended format
return callback_fn_wrap_ex;
}
return NULL;
}

// Helper function to handle return values from |BIO_read|, |BIO_write|,
// |BIO_gets|, and |BIO_puts| operations.
static int handle_callback_return(BIO *bio, int oper, const void *buf,
int len, int ret) {

size_t processed = 0;

if (ret > 0) {
if (oper == BIO_CB_READ || oper == BIO_CB_GETS) {
bio->num_read += ret;
} else if (oper == BIO_CB_WRITE || oper == BIO_CB_PUTS) {
bio->num_write += ret;
}
// |callback_ex| receives the number of bytes processed via the |processed| parameter,
// while the legacy callback receives this information through both |argi| and |ret|.
// When using the legacy callback, the |processed| value will be mapped back to |ret|.
processed = ret;
ret = 1;
}

BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long callback_ret = cb(bio, oper | BIO_CB_RETURN, buf, len, 0, 0L, ret, &processed);
if (callback_ret > INT_MAX || callback_ret < INT_MIN) {
return -1;
}
ret = (int)callback_ret;
}


if (ret > 0) {
if (processed > INT_MAX) {
ret = -1; // Value too large to represent as int
} else {
ret = -1;
ret = (int)processed;
}
}

return ret;
}

Expand All @@ -112,6 +192,7 @@ BIO *BIO_new(const BIO_METHOD *method) {
ret->shutdown = 1;
ret->references = 1;
ret->callback_ex = NULL;
ret->callback = NULL;
CRYPTO_new_ex_data(&ret->ex_data);

if (method->create != NULL && !method->create(ret)) {
Expand All @@ -135,8 +216,10 @@ int BIO_free(BIO *bio) {
if (bio->method != NULL && bio->method->destroy != NULL) {
bio->method->destroy(bio);
}
if (HAS_CALLBACK(bio)) {
long ret = bio->callback_ex(bio, BIO_CB_FREE, NULL, 0, 0, 0L, 1L, NULL);

BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long ret = cb(bio, BIO_CB_FREE, NULL, 0, 0, 0L, 1L, NULL);
if (ret <= 0) {
if (ret >= INT_MIN) {
return (int)ret;
Expand Down Expand Up @@ -165,7 +248,7 @@ void BIO_free_all(BIO *bio) {
}

int BIO_read(BIO *bio, void *buf, int len) {
int ret = 0;

if (bio == NULL || bio->method == NULL || bio->method->bread == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
Expand All @@ -174,28 +257,24 @@ int BIO_read(BIO *bio, void *buf, int len) {
return 0;
}

if (HAS_CALLBACK(bio)) {
long callback_ret = bio->callback_ex(bio, BIO_CB_READ, buf, len, 0, 0L, 1L, NULL);
BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long callback_ret = cb(bio, BIO_CB_READ, buf, len, 0, 0L, 1L, NULL);
if (callback_ret <= 0) {
if (callback_ret >= INT_MIN) {
return (int)callback_ret;
}
return INT_MIN;
}
}

if (!bio->init) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
return -2;
}
ret = bio->method->bread(bio, buf, len);
if (ret > 0) {
bio->num_read += ret;
}

ret = call_bio_callback_with_processed(bio, BIO_CB_READ | BIO_CB_RETURN, buf,
len, ret);
int ret = bio->method->bread(bio, buf, len);

return ret;
return handle_callback_return(bio, BIO_CB_READ, buf, len, ret);
}

int BIO_read_ex(BIO *bio, void *data, size_t data_len, size_t *read_bytes) {
Expand Down Expand Up @@ -228,30 +307,28 @@ int BIO_gets(BIO *bio, char *buf, int len) {
return 0;
}

if (HAS_CALLBACK(bio)) {
long callback_ret = bio->callback_ex(bio, BIO_CB_GETS, buf, len, 0, 0L, 1L, NULL);
BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long callback_ret = cb(bio, BIO_CB_GETS, buf, len, 0, 0L, 1L, NULL);
if (callback_ret <= 0) {
if (callback_ret >= INT_MIN) {
return (int)callback_ret;
}
return INT_MIN;
}
}

if (!bio->init) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
return -2;
}
int ret = bio->method->bgets(bio, buf, len);
if (ret > 0) {
bio->num_read += ret;
}
ret = call_bio_callback_with_processed(bio, BIO_CB_GETS | BIO_CB_RETURN, buf,
len, ret);
return ret;

return handle_callback_return(bio, BIO_CB_GETS, buf, len, ret);
}

int BIO_write(BIO *bio, const void *in, int inl) {
int ret = 0;

if (bio == NULL || bio->method == NULL || bio->method->bwrite == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
Expand All @@ -260,8 +337,9 @@ int BIO_write(BIO *bio, const void *in, int inl) {
return 0;
}

if (HAS_CALLBACK(bio)) {
long callback_ret = bio->callback_ex(bio, BIO_CB_WRITE, in, inl, 0, 0L, 1L, NULL);
BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long callback_ret = cb(bio, BIO_CB_WRITE, in, inl, 0, 0L, 1L, NULL);
if (callback_ret <= 0) {
if (callback_ret >= INT_MIN) {
return (int)callback_ret;
Expand All @@ -274,15 +352,9 @@ int BIO_write(BIO *bio, const void *in, int inl) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNINITIALIZED);
return -2;
}
ret = bio->method->bwrite(bio, in, inl);
if (ret > 0) {
bio->num_write += ret;
}

ret = call_bio_callback_with_processed(bio, BIO_CB_WRITE | BIO_CB_RETURN, in,
inl, ret);
int ret = bio->method->bwrite(bio, in, inl);

return ret;
return handle_callback_return(bio, BIO_CB_WRITE, in, inl, ret);
}

int BIO_write_ex(BIO *bio, const void *data, size_t data_len, size_t *written_bytes) {
Expand Down Expand Up @@ -333,8 +405,10 @@ int BIO_puts(BIO *bio, const char *in) {
OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD);
return -2;
}
if(HAS_CALLBACK(bio)) {
long callback_ret = bio->callback_ex(bio, BIO_CB_PUTS, in, 0, 0, 0L, 1L, NULL);

BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
long callback_ret = cb(bio, BIO_CB_PUTS, in, 0, 0, 0L, 1L, NULL);
if (callback_ret <= 0) {
if (callback_ret >= INT_MIN) {
return (int)callback_ret;
Expand All @@ -348,6 +422,7 @@ int BIO_puts(BIO *bio, const char *in) {
return -2;
}
int ret = 0;

if (bio->method->bputs != NULL) {
ret = bio->method->bputs(bio, in);
} else {
Expand All @@ -359,13 +434,8 @@ int BIO_puts(BIO *bio, const char *in) {
}
ret = bio->method->bwrite(bio, in, len);
}
if (ret > 0) {
bio->num_write += ret;
}
ret = call_bio_callback_with_processed(bio, BIO_CB_PUTS | BIO_CB_RETURN,
in, 0, ret);

return ret;

return handle_callback_return(bio, BIO_CB_PUTS, in, 0, ret);
}

int BIO_flush(BIO *bio) {
Expand All @@ -382,18 +452,24 @@ long BIO_ctrl(BIO *bio, int cmd, long larg, void *parg) {
return -2;
}
long ret = 0;
if (HAS_CALLBACK(bio)) {
ret = bio->callback_ex(bio, BIO_CB_CTRL, parg, 0, cmd, larg, 1L, NULL);

BIO_callback_fn_ex cb = get_callback(bio);
if (cb != NULL) {
ret = cb(bio, BIO_CB_CTRL, parg, 0, cmd, larg, 1L, NULL);
if (ret <= 0) {
return ret;
}
}


ret = bio->method->ctrl(bio, cmd, larg, parg);
if (HAS_CALLBACK(bio)) {
ret = bio->callback_ex(bio, BIO_CB_CTRL | BIO_CB_RETURN, parg, 0, cmd, larg,

cb = get_callback(bio);
if (cb != NULL) {
ret = cb(bio, BIO_CB_CTRL | BIO_CB_RETURN, parg, 0, cmd, larg,
ret, NULL);
}

return ret;
}

Expand Down Expand Up @@ -922,6 +998,10 @@ void BIO_set_callback_ex(BIO *bio, BIO_callback_fn_ex callback) {
bio->callback_ex = callback;
}

void BIO_set_callback(BIO *bio, BIO_callback_fn callback) {
bio->callback = callback;
}

void BIO_set_callback_arg(BIO *bio, char *arg) {
bio->cb_arg = arg;
}
Expand Down
Loading
Loading