Quanlong Huang created THRIFT-5716:
--------------------------------------

             Summary: 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


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