diff --git a/mojo/core/channel.cc b/mojo/core/channel.cc index 12a65307693e7..efaa0fa4b2cc3 100644 --- a/mojo/core/channel.cc +++ b/mojo/core/channel.cc @@ -939,9 +939,18 @@ class Channel::ReadBuffer { // Ensures the ReadBuffer has enough contiguous space allocated to hold // |num_bytes| more bytes; returns the address of the first available byte. char* Reserve(size_t num_bytes) { - if (num_occupied_bytes_ + num_bytes > size_) { - size_ = std::max(static_cast(size_ * kGrowthFactor), - num_occupied_bytes_ + num_bytes); + // `num_bytes` is derived from the wire (IpczHeader::num_bytes, a uint32_t + // controlled by the remote process) via the size_hint plumbed from + // TryDispatchMessage -> OnReadComplete -> GetReadBuffer. On 32-bit targets + // an unchecked add can wrap below `size_`, skipping the grow branch and + // returning a past-the-end pointer that the caller hands to recvmsg/ + // ReadFile with a multi-gigabyte length. Use checked arithmetic so an + // attacker-supplied value triggers a safe crash instead of a heap OOB + // write. + const size_t required = + base::CheckAdd(num_occupied_bytes_, num_bytes).ValueOrDie(); + if (required > size_) { + size_ = std::max(static_cast(size_ * kGrowthFactor), required); AlignedBuffer new_data = MakeAlignedBuffer(size_); new_data.copy_prefix_from(data_.first(num_occupied_bytes_)); data_ = std::move(new_data);