[ 
https://issues.apache.org/jira/browse/THRIFT-5716?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Quanlong Huang resolved THRIFT-5716.
------------------------------------
    Fix Version/s: 0.19.0
       Resolution: Fixed

> TMemoryBuffer resizing might shrink the buffer size due to uint32_t overflow
> ----------------------------------------------------------------------------
>
>                 Key: THRIFT-5716
>                 URL: https://issues.apache.org/jira/browse/THRIFT-5716
>             Project: Thrift
>          Issue Type: Bug
>          Components: C++ - Library
>    Affects Versions: 0.14.0
>            Reporter: Quanlong Huang
>            Assignee: Quanlong Huang
>            Priority: Critical
>             Fix For: 0.19.0
>
>          Time Spent: 1h
>  Remaining Estimate: 0h
>
> In TMemoryBuffer::ensureCanWrite(), the calculation of 'required_buffer_size' 
> might overflow since it's the sum of two uint32_t values. The type of 
> 'required_buffer_size' should be uint64_t.
> {code:cpp}
> void TMemoryBuffer::ensureCanWrite(uint32_t len) {
>   // Check available space
>   uint32_t avail = available_write();
>   if (len <= avail) {
>     return;
>   }
>   if (!owner_) {
>     throw TTransportException("Insufficient space in external MemoryBuffer");
>   }
>   // Grow the buffer as necessary.
>   const uint32_t current_used = bufferSize_ - avail;
>   const uint32_t required_buffer_size = len + current_used; // <-- This could 
> overflow
>   if (required_buffer_size > maxBufferSize_) {
>     throw TTransportException(TTransportException::BAD_ARGS,
>                               "Internal buffer size overflow when requesting 
> a buffer of size " + std::to_string(required_buffer_size));
>   }
>   // Always grow to the next bigger power of two:
>   const double suggested_buffer_size = 
> std::exp2(std::ceil(std::log2(required_buffer_size)));
>   // Unless the power of two exceeds maxBufferSize_:
>   const uint64_t new_size = 
> static_cast<uint64_t>((std::min)(suggested_buffer_size, 
> static_cast<double>(maxBufferSize_)));
>   // Allocate into a new pointer so we don't bork ours if it fails.
>   auto* new_buffer = static_cast<uint8_t*>(std::realloc(buffer_, 
> static_cast<std::size_t>(new_size)));
>   if (new_buffer == nullptr) {
>     throw std::bad_alloc();
>   }
>   rBase_ = new_buffer + (rBase_ - buffer_);
>   rBound_ = new_buffer + (rBound_ - buffer_);
>   wBase_ = new_buffer + (wBase_ - buffer_);
>   wBound_ = new_buffer + new_size;
>   // Note: with realloc() we do not need to free the previous buffer:
>   buffer_ = new_buffer;
>   bufferSize_ = static_cast<uint32_t>(new_size);
> } {code}
> Overflow example:
> {noformat}
> len=1066587646, current_used=4167372953, 
> required_buffer_size=938993303{noformat}
> The buffer is not expanded if 'required_buffer_size' overflow. 
> TMemoryBuffer::writeSlow() would finally writes to invalid address and crash 
> the process:
> {code:cpp}
> void TMemoryBuffer::writeSlow(const uint8_t* buf, uint32_t len) {
>   ensureCanWrite(len);
>   // Copy into the buffer and increment wBase_.
>   memcpy(wBase_, buf, len);  // <-- This could crash
>   wBase_ += len;
> } {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to