Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a6954573 authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

Merge "Fix handling of possible bad gnu_debugdata_size."

parents 1451e0f1 8726d3a4
Loading
Loading
Loading
Loading
+37 −14
Original line number Diff line number Diff line
@@ -78,10 +78,31 @@ Memory* ElfInterface::CreateGnuDebugdataMemory() {
  CrcGenerateTable();
  Crc64GenerateTable();

  std::vector<uint8_t> src(gnu_debugdata_size_);
  if (!memory_->ReadFully(gnu_debugdata_offset_, src.data(), gnu_debugdata_size_)) {
    gnu_debugdata_offset_ = 0;
    gnu_debugdata_size_ = static_cast<uint64_t>(-1);
  // Verify the request is not larger than the max size_t value.
  if (gnu_debugdata_size_ > SIZE_MAX) {
    return nullptr;
  }
  size_t initial_buffer_size;
  if (__builtin_mul_overflow(5, gnu_debugdata_size_, &initial_buffer_size)) {
    return nullptr;
  }

  size_t buffer_increment;
  if (__builtin_mul_overflow(2, gnu_debugdata_size_, &buffer_increment)) {
    return nullptr;
  }

  std::unique_ptr<uint8_t[]> src(new (std::nothrow) uint8_t[gnu_debugdata_size_]);
  if (src.get() == nullptr) {
    return nullptr;
  }

  std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
  if (!dst->Resize(initial_buffer_size)) {
    return nullptr;
  }

  if (!memory_->ReadFully(gnu_debugdata_offset_, src.get(), gnu_debugdata_size_)) {
    return nullptr;
  }

@@ -89,21 +110,23 @@ Memory* ElfInterface::CreateGnuDebugdataMemory() {
  CXzUnpacker state;
  alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
  alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };

  XzUnpacker_Construct(&state, &alloc);

  std::unique_ptr<MemoryBuffer> dst(new MemoryBuffer);
  int return_val;
  size_t src_offset = 0;
  size_t dst_offset = 0;
  ECoderStatus status;
  dst->Resize(5 * gnu_debugdata_size_);
  do {
    size_t src_remaining = src.size() - src_offset;
    size_t src_remaining = gnu_debugdata_size_ - src_offset;
    size_t dst_remaining = dst->Size() - dst_offset;
    if (dst_remaining < 2 * gnu_debugdata_size_) {
      dst->Resize(dst->Size() + 2 * gnu_debugdata_size_);
      dst_remaining += 2 * gnu_debugdata_size_;
    if (dst_remaining < buffer_increment) {
      size_t new_size;
      if (__builtin_add_overflow(dst->Size(), buffer_increment, &new_size) ||
          !dst->Resize(new_size)) {
        XzUnpacker_Free(&state);
        return nullptr;
      }
      dst_remaining += buffer_increment;
    }
    return_val = XzUnpacker_Code(&state, dst->GetPtr(dst_offset), &dst_remaining, &src[src_offset],
                                 &src_remaining, true, CODER_FINISH_ANY, &status);
@@ -112,13 +135,13 @@ Memory* ElfInterface::CreateGnuDebugdataMemory() {
  } while (return_val == SZ_OK && status == CODER_STATUS_NOT_FINISHED);
  XzUnpacker_Free(&state);
  if (return_val != SZ_OK || !XzUnpacker_IsStreamWasFinished(&state)) {
    gnu_debugdata_offset_ = 0;
    gnu_debugdata_size_ = static_cast<uint64_t>(-1);
    return nullptr;
  }

  // Shrink back down to the exact size.
  dst->Resize(dst_offset);
  if (!dst->Resize(dst_offset)) {
    return nullptr;
  }

  return dst.release();
}
+4 −4
Original line number Diff line number Diff line
@@ -206,12 +206,12 @@ std::shared_ptr<Memory> Memory::CreateOfflineMemory(const uint8_t* data, uint64_
}

size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
  if (addr >= raw_.size()) {
  if (addr >= size_) {
    return 0;
  }

  size_t bytes_left = raw_.size() - static_cast<size_t>(addr);
  const unsigned char* actual_base = static_cast<const unsigned char*>(raw_.data()) + addr;
  size_t bytes_left = size_ - static_cast<size_t>(addr);
  const unsigned char* actual_base = static_cast<const unsigned char*>(raw_) + addr;
  size_t actual_len = std::min(bytes_left, size);

  memcpy(dst, actual_base, actual_len);
@@ -219,7 +219,7 @@ size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
}

uint8_t* MemoryBuffer::GetPtr(size_t offset) {
  if (offset < raw_.size()) {
  if (offset < size_) {
    return &raw_[offset];
  }
  return nullptr;
+13 −4
Original line number Diff line number Diff line
@@ -29,18 +29,27 @@ namespace unwindstack {
class MemoryBuffer : public Memory {
 public:
  MemoryBuffer() = default;
  virtual ~MemoryBuffer() = default;
  virtual ~MemoryBuffer() { free(raw_); }

  size_t Read(uint64_t addr, void* dst, size_t size) override;

  uint8_t* GetPtr(size_t offset);

  void Resize(size_t size) { raw_.resize(size); }
  bool Resize(size_t size) {
    raw_ = reinterpret_cast<uint8_t*>(realloc(raw_, size));
    if (raw_ == nullptr) {
      size_ = 0;
      return false;
    }
    size_ = size;
    return true;
  }

  uint64_t Size() { return raw_.size(); }
  uint64_t Size() { return size_; }

 private:
  std::vector<uint8_t> raw_;
  uint8_t* raw_ = nullptr;
  size_t size_ = 0;
};

}  // namespace unwindstack
+3 −0
Original line number Diff line number Diff line
@@ -105,6 +105,9 @@ class ElfInterfaceFake : public ElfInterface {
  void FakeSetDynamicVaddrStart(uint64_t vaddr) { dynamic_vaddr_start_ = vaddr; }
  void FakeSetDynamicVaddrEnd(uint64_t vaddr) { dynamic_vaddr_end_ = vaddr; }

  void FakeSetGnuDebugdataOffset(uint64_t offset) { gnu_debugdata_offset_ = offset; }
  void FakeSetGnuDebugdataSize(uint64_t size) { gnu_debugdata_size_ = size; }

 private:
  std::unordered_map<std::string, uint64_t> globals_;
  std::string fake_build_id_;
+19 −0
Original line number Diff line number Diff line
@@ -1944,4 +1944,23 @@ TEST_F(ElfInterfaceTest, get_load_bias_exec_negative_64) {
  CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x5000, 0x1000, -0x4000);
}

TEST_F(ElfInterfaceTest, huge_gnu_debugdata_size) {
  ElfInterfaceFake interface(nullptr);

  interface.FakeSetGnuDebugdataOffset(0x1000);
  interface.FakeSetGnuDebugdataSize(0xffffffffffffffffUL);
  ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);

  interface.FakeSetGnuDebugdataSize(0x4000000000000UL);
  ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);

  // This should exceed the size_t value of the first allocation.
#if defined(__LP64__)
  interface.FakeSetGnuDebugdataSize(0x3333333333333334ULL);
#else
  interface.FakeSetGnuDebugdataSize(0x33333334);
#endif
  ASSERT_TRUE(interface.CreateGnuDebugdataMemory() == nullptr);
}

}  // namespace unwindstack