Commit cc5f85e1660bd3d7deacebe81fbc2fd27560d388
1 parent
8609c607
Fix SSL crash on SSL_write retry
When SSL wants the app to retry, it stands to reason that the local write buffer needs to be grown (read: reallocated) to hold the application bytes. At that point, the buffer is no longer where it was originally. This fixes segfaults.
Showing
3 changed files
with
15 additions
and
11 deletions
iowrapper.cpp
| @@ -22,8 +22,8 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. | @@ -22,8 +22,8 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. | ||
| 22 | #include "logger.h" | 22 | #include "logger.h" |
| 23 | #include "client.h" | 23 | #include "client.h" |
| 24 | 24 | ||
| 25 | -IncompleteSslWrite::IncompleteSslWrite(const void *buf, size_t nbytes) : | ||
| 26 | - buf(buf), | 25 | +IncompleteSslWrite::IncompleteSslWrite(size_t nbytes) : |
| 26 | + valid(true), | ||
| 27 | nbytes(nbytes) | 27 | nbytes(nbytes) |
| 28 | { | 28 | { |
| 29 | 29 | ||
| @@ -31,13 +31,13 @@ IncompleteSslWrite::IncompleteSslWrite(const void *buf, size_t nbytes) : | @@ -31,13 +31,13 @@ IncompleteSslWrite::IncompleteSslWrite(const void *buf, size_t nbytes) : | ||
| 31 | 31 | ||
| 32 | void IncompleteSslWrite::reset() | 32 | void IncompleteSslWrite::reset() |
| 33 | { | 33 | { |
| 34 | - buf = nullptr; | 34 | + valid = false; |
| 35 | nbytes = 0; | 35 | nbytes = 0; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | bool IncompleteSslWrite::hasPendingWrite() const | 38 | bool IncompleteSslWrite::hasPendingWrite() const |
| 39 | { | 39 | { |
| 40 | - return buf != nullptr; | 40 | + return valid; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | void IncompleteWebsocketRead::reset() | 43 | void IncompleteWebsocketRead::reset() |
| @@ -264,7 +264,6 @@ ssize_t IoWrapper::writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWra | @@ -264,7 +264,6 @@ ssize_t IoWrapper::writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWra | ||
| 264 | } | 264 | } |
| 265 | else | 265 | else |
| 266 | { | 266 | { |
| 267 | - const void *buf_ = buf; | ||
| 268 | size_t nbytes_ = nbytes; | 267 | size_t nbytes_ = nbytes; |
| 269 | 268 | ||
| 270 | /* | 269 | /* |
| @@ -273,7 +272,6 @@ ssize_t IoWrapper::writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWra | @@ -273,7 +272,6 @@ ssize_t IoWrapper::writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWra | ||
| 273 | */ | 272 | */ |
| 274 | if (this->incompleteSslWrite.hasPendingWrite()) | 273 | if (this->incompleteSslWrite.hasPendingWrite()) |
| 275 | { | 274 | { |
| 276 | - buf_ = this->incompleteSslWrite.buf; | ||
| 277 | nbytes_ = this->incompleteSslWrite.nbytes; | 275 | nbytes_ = this->incompleteSslWrite.nbytes; |
| 278 | } | 276 | } |
| 279 | 277 | ||
| @@ -285,7 +283,7 @@ ssize_t IoWrapper::writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWra | @@ -285,7 +283,7 @@ ssize_t IoWrapper::writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWra | ||
| 285 | 283 | ||
| 286 | ERR_clear_error(); | 284 | ERR_clear_error(); |
| 287 | char sslErrorBuf[OPENSSL_ERROR_STRING_SIZE]; | 285 | char sslErrorBuf[OPENSSL_ERROR_STRING_SIZE]; |
| 288 | - n = SSL_write(ssl, buf_, nbytes_); | 286 | + n = SSL_write(ssl, buf, nbytes_); |
| 289 | 287 | ||
| 290 | if (n <= 0) | 288 | if (n <= 0) |
| 291 | { | 289 | { |
| @@ -295,7 +293,7 @@ ssize_t IoWrapper::writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWra | @@ -295,7 +293,7 @@ ssize_t IoWrapper::writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWra | ||
| 295 | { | 293 | { |
| 296 | logger->logf(LOG_DEBUG, "SSL Write is incomplete: %d. Will be retried later.", err); | 294 | logger->logf(LOG_DEBUG, "SSL Write is incomplete: %d. Will be retried later.", err); |
| 297 | *error = IoWrapResult::Wouldblock; | 295 | *error = IoWrapResult::Wouldblock; |
| 298 | - IncompleteSslWrite sslAction(buf_, nbytes_); | 296 | + IncompleteSslWrite sslAction(nbytes_); |
| 299 | this->incompleteSslWrite = sslAction; | 297 | this->incompleteSslWrite = sslAction; |
| 300 | if (err == SSL_ERROR_WANT_READ) | 298 | if (err == SSL_ERROR_WANT_READ) |
| 301 | this->sslWriteWantsRead = true; | 299 | this->sslWriteWantsRead = true; |
iowrapper.h
| @@ -56,17 +56,21 @@ enum class WebsocketOpcode | @@ -56,17 +56,21 @@ enum class WebsocketOpcode | ||
| 56 | Unknown = 0xF | 56 | Unknown = 0xF |
| 57 | }; | 57 | }; |
| 58 | 58 | ||
| 59 | -/* | 59 | +/** |
| 60 | + * @brief The IncompleteSslWrite struct facilities the SSL retry | ||
| 61 | + * | ||
| 60 | * OpenSSL doc: "When a write function call has to be repeated because SSL_get_error(3) returned | 62 | * OpenSSL doc: "When a write function call has to be repeated because SSL_get_error(3) returned |
| 61 | * SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be repeated with the same arguments" | 63 | * SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be repeated with the same arguments" |
| 64 | + * | ||
| 65 | + * Note that we use SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER. | ||
| 62 | */ | 66 | */ |
| 63 | struct IncompleteSslWrite | 67 | struct IncompleteSslWrite |
| 64 | { | 68 | { |
| 65 | - const void *buf = nullptr; | 69 | + bool valid = false; |
| 66 | size_t nbytes = 0; | 70 | size_t nbytes = 0; |
| 67 | 71 | ||
| 68 | IncompleteSslWrite() = default; | 72 | IncompleteSslWrite() = default; |
| 69 | - IncompleteSslWrite(const void *buf, size_t nbytes); | 73 | + IncompleteSslWrite(size_t nbytes); |
| 70 | bool hasPendingWrite() const; | 74 | bool hasPendingWrite() const; |
| 71 | 75 | ||
| 72 | void reset(); | 76 | void reset(); |
listener.cpp
| @@ -86,6 +86,8 @@ void Listener::loadCertAndKeyFromConfig() | @@ -86,6 +86,8 @@ void Listener::loadCertAndKeyFromConfig() | ||
| 86 | sslctx = std::make_unique<SslCtxManager>(); | 86 | sslctx = std::make_unique<SslCtxManager>(); |
| 87 | SSL_CTX_set_options(sslctx->get(), SSL_OP_NO_SSLv3); // TODO: config option | 87 | SSL_CTX_set_options(sslctx->get(), SSL_OP_NO_SSLv3); // TODO: config option |
| 88 | SSL_CTX_set_options(sslctx->get(), SSL_OP_NO_TLSv1); // TODO: config option | 88 | SSL_CTX_set_options(sslctx->get(), SSL_OP_NO_TLSv1); // TODO: config option |
| 89 | + | ||
| 90 | + SSL_CTX_set_mode(sslctx->get(), SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); | ||
| 89 | } | 91 | } |
| 90 | 92 | ||
| 91 | if (SSL_CTX_use_certificate_file(sslctx->get(), sslFullchain.c_str(), SSL_FILETYPE_PEM) != 1) | 93 | if (SSL_CTX_use_certificate_file(sslctx->get(), sslFullchain.c_str(), SSL_FILETYPE_PEM) != 1) |