Allocate small TLS read buffers inline.
Our TLS read patterns are always read(5); read(record_size); read(5);
read(record_size); ...;. Allocate the 5-byte reads inline in SSLBuffer.
This avoids bouncing on a 5-byte malloc to learn a socket is idle and
avoids calling malloc twice on each record.
This costs a few bytes but means we malloc once per record, rather than
twice per record + once each time the state machine is run while idle.
Change-Id: I4f6dafe4141cbb890b921a5fa8d528c1fb98a0b4
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/39004
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/internal.h b/ssl/internal.h
index 41135e3..7ef810e 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1239,6 +1239,11 @@
uint16_t size_ = 0;
// cap_ is how much memory beyond |buf_| + |offset_| is available.
uint16_t cap_ = 0;
+ // inline_buf_ is a static buffer for short reads.
+ uint8_t inline_buf_[SSL3_RT_HEADER_LENGTH];
+ // buf_allocated_ is true if |buf_| points to allocated data and must be freed
+ // or false if it points into |inline_buf_|.
+ bool buf_allocated_ = false;
};
// ssl_read_buffer_extend_to extends the read buffer to the desired length. For
diff --git a/ssl/ssl_buffer.cc b/ssl/ssl_buffer.cc
index 49ecf90..d73055f 100644
--- a/ssl/ssl_buffer.cc
+++ b/ssl/ssl_buffer.cc
@@ -37,8 +37,11 @@
"SSL3_ALIGN_PAYLOAD must be a power of 2");
void SSLBuffer::Clear() {
- free(buf_); // Allocated with malloc().
+ if (buf_allocated_) {
+ free(buf_); // Allocated with malloc().
+ }
buf_ = nullptr;
+ buf_allocated_ = false;
offset_ = 0;
size_ = 0;
cap_ = 0;
@@ -54,27 +57,43 @@
return true;
}
- // Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment.
- //
- // Since this buffer gets allocated quite frequently and doesn't contain any
- // sensitive data, we allocate with malloc rather than |OPENSSL_malloc| and
- // avoid zeroing on free.
- uint8_t *new_buf = (uint8_t *)malloc(new_cap + SSL3_ALIGN_PAYLOAD - 1);
- if (new_buf == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return false;
+ uint8_t *new_buf;
+ bool new_buf_allocated;
+ size_t new_offset;
+ if (new_cap <= sizeof(inline_buf_)) {
+ // This function is called twice per TLS record, first for the five-byte
+ // header. To avoid allocating twice, use an inline buffer for short inputs.
+ new_buf = inline_buf_;
+ new_buf_allocated = false;
+ new_offset = 0;
+ } else {
+ // Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment.
+ //
+ // Since this buffer gets allocated quite frequently and doesn't contain any
+ // sensitive data, we allocate with malloc rather than |OPENSSL_malloc| and
+ // avoid zeroing on free.
+ new_buf = (uint8_t *)malloc(new_cap + SSL3_ALIGN_PAYLOAD - 1);
+ if (new_buf == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return false;
+ }
+ new_buf_allocated = true;
+
+ // Offset the buffer such that the record body is aligned.
+ new_offset =
+ (0 - header_len - (uintptr_t)new_buf) & (SSL3_ALIGN_PAYLOAD - 1);
}
- // Offset the buffer such that the record body is aligned.
- size_t new_offset =
- (0 - header_len - (uintptr_t)new_buf) & (SSL3_ALIGN_PAYLOAD - 1);
+ // Note if the both old and new buffer are inline, the source and destination
+ // may alias.
+ OPENSSL_memmove(new_buf + new_offset, buf_ + offset_, size_);
- if (buf_ != NULL) {
- OPENSSL_memcpy(new_buf + new_offset, buf_ + offset_, size_);
+ if (buf_allocated_) {
free(buf_); // Allocated with malloc().
}
buf_ = new_buf;
+ buf_allocated_ = new_buf_allocated;
offset_ = new_offset;
cap_ = new_cap;
return true;