Commit cc5f85e1660bd3d7deacebe81fbc2fd27560d388

Authored by Wiebe Cazemier
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.
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)