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
BIO_sendmmsg/BIO_recvmmsg (API only) #18923
Conversation
doc/man3/BIO_sendmmsg.pod
Outdated
a packed system error code is returned. The macro BIO_IS_ERRNO() evaluates to 1 | ||
if a BIO_sendmmsg() or BIO_recvmmsg() return value represents a system error | ||
code. BIO_UNPACK_ERRNO() should then be used to obtain the system error code | ||
(EWOULDBLOCK, etc.) from the return value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder why we do this. In other BIO calls we just signal error and let the caller examine errno directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would need to add set_last_socket_error
but I see no portability issues here.
Or we could add an int *err
parameter and avoid depending on thread local state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we ever use this for DTLS then we will need to ensure errno is set for a system call failure because that is what existing applications will expect. Plausibly we could do that in libssl rather than in the BIO code. So, I'm in two minds about this.
Why would we need set_last_socket_error
?
Updated. |
doc/man3/BIO_sendmmsg.pod
Outdated
a packed system error code is returned. The macro BIO_IS_ERRNO() evaluates to 1 | ||
if a BIO_sendmmsg() or BIO_recvmmsg() return value represents a system error | ||
code. BIO_UNPACK_ERRNO() should then be used to obtain the system error code | ||
(EWOULDBLOCK, etc.) from the return value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we ever use this for DTLS then we will need to ensure errno is set for a system call failure because that is what existing applications will expect. Plausibly we could do that in libssl rather than in the BIO code. So, I'm in two minds about this.
Why would we need set_last_socket_error
?
Alternatives to the current API:
I don't see much use of Not sure what you mean about DTLS. This is a new API without existing users. If there are semantics of the DTLS APIs we need to preserve those belong in libssl... |
I've updated this to have a new |
Its a new API that could be used by some future alteration to the DTLS code. The libssl DTLS code currently calls the If we at some point in the future modify libssl to call Modifying libssl to set errno instead would work, but is kind of weird and not ideal. The errno behaviour is specific to BIOs that actually make syscalls and doesn't apply to (say) a mem BIO. Libssl doesn't actually know what kind of BIO it is dealing with.
How is |
So basically we have a situation where:
This is unfortunate. I feel like we should fix this in 4.0, probably by zeroing errno after syscalls. Nonetheless, we have to support the current expectation... Some things worth noting, however. If applications are depending on errno in this way, can we even be confident that switching from I suspect the safest thing would be to make use of sendmmsg/recvmmsg in libssl opt-in, requiring the application to explicitly enable the new semantics (and forego use of the errno side channel).
|
(But leaving all of the above aside, since |
That's a fair summary. Although whether it was accidental or not is lost in the mists of time. I think this behaviour has probably been there since SSLeay times (i.e. before OpenSSL even existed). It is so entrenched that it is now documented. From the man page for
|
That's some interesting history... So basically the question for now is whether
The only concern I have is making libssl touch errno, which doesn't necessarily exist on all platforms (and needs to be WSASetLastError on Win32). Though it looks like it already does in one case. But this can be readily abstracted by adding a So all libssl would have to do, if it decides to preserve these semantics when using sendmmsg/recvmmsg would be: int err;
num_processed = BIO_sendmmsg(..., &err);
set_last_socket_error(err); /* The OS socket error variable is part of the SSL_read API */ |
I can live with libssl doing this. However, I have another thought (which may be slightly contradictory to my previous thought, but still needs to be considered). Should we be using OS specific error values at all in a new API? Or should we convert them to an OpenSSL error value of some form. Part of the point of the BIO abstraction is to abstract away system specific details. Applications written to use OpenSSL APIs should be portable. If you write an OpenSSL app on one platform, it should more-or-less work unmodified on a different platform (at least as far as the OpenSSL specific bits are concerned). By incorporating system specific error codes into the API we are breaking that. Users of the API will have to have different code to handle different platforms that they run on. |
Matt, I don't think converting OS specific errors is a good idea. When you have os specific error, you can google the possible reasons more effectively. |
Option 1: Use our own error codesI was thinking about this yesterday. It does seem like it would be better in principle to use our own error codes. My main concern is then we would have e.g. a function with a switch statement mapping from OS error codes to our own; but then what if the OS returns an error code we didn't add support for? Not a massive deal since OSes don't want to break compatibility either and it would be odd for them to add a new code in an existing syscall. But new platforms may have OS-specific error codes we don't handle. These would presumably be translated to some kind of Option 2: Define-based abstractionAnother option is we do define our own error codes, but these just forward to OS-specific ones: #ifdef OPENSSL_SYS_WINDOWS
#define BIO_ERR_WOULDBLOCK WSAEWOULDBLOCK
#else
#define BIO_ERR_WOULDBLOCK EWOULDBLOCK
#endif The numeric But I don't think this works either; there may be multiple error codes on a given OS corresponding to one of our abstracted error codes (for example, Option 3: Predicate and opaque system-specific value
typedef int BIO_ERR;
int BIO_ERR_is_would_block(BIO_ERR err);
int BIO_ERR_is_conn_refused(BIO_ERR err);
int BIO_ERR_is_intr(BIO_ERR err);
int BIO_ERR_to_socket_error(BIO_ERR err);
BIO_ERR BIO_ERR_from_socket_error(int err); I think this is probably the best option:
Might be a good idea to make BIO_ERR 64-bit or a structure with a couple of opaque ints in just in case. I'll update this PR soon with a prototype of this. |
Option 3 sounds plausible, but please consider how it interacts/intersects with numerous other BIO macros/functions in this area. E.g.
In particular codes like |
Please, please, leave a plain option to get a system errno/LastError. Otherwise debugging becomes much more difficult. |
@beldmit Yes, this will be supported via BIO_ERR_to_socket_error. @mattcaswell Most of the BIO calls read or write state on the BIO so are incompatible with our objectives of supporting concurrent use for the mmsg methods. BIO_sock_non_fatal_error is more plausible though does hold us to using an int rather than a structure. Anyway, I'll come up with a proposal. |
Yes, good point. |
New update. Trying to avoid overkill and reinventing our existing error code infrastructure. |
Ping? This is holding a lot up now. |
doc/man3/BIO_sendmmsg.pod
Outdated
retrieved using C<ERR_GET_REASON(errcode)>. The packed error code can be | ||
retrieved by calling L<ERR_peek_last_error(3)> after the call to BIO_sendmmsg() | ||
or BIO_recvmmsg() returns 0. Whether the error is transient can be determined by | ||
passing the packed error code to BIO_err_is_non_fatal(). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would a non-socket based custom BIO implementation signal a retry (e.g. a mem BIO)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Via the same means? This doesn't rely on OS functionality so there's no reason alternative BIO implementations can't signal the same way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BIO_err_is_non_fatal()
checks whether the error is ERR_LIB_SYS
and if so what sys error code it is. It seems a bit weird for a mem bio to be indicating a syscall error when it hasn't made any syscalls?
Updated:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just two nits.
Updated. |
This pull request is ready to merge |
Merged to master. |
Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from #18923)
Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#18923)
To make merging this easier, as a first step I've separated out the API into a separate PR. There are no implementations provided here, though a third party could do so using the BIO APIs.
An updated version of the BIO_dgram implementation PR without the Windows compatibility issues will follow.