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

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

Merge "Add overflow checks in Memory objects."

parents 23e30839 f447c8eb
Loading
Loading
Loading
Loading
+17 −17
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) {
  uint64_t offset = ehdr.e_phoff;
  for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
    PhdrType phdr;
    if (!memory_->Read(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
    if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
      return false;
    }

@@ -54,20 +54,20 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) {
    case PT_LOAD:
    {
      // Get the flags first, if this isn't an executable header, ignore it.
      if (!memory_->Read(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
      if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
        return false;
      }
      if ((phdr.p_flags & PF_X) == 0) {
        continue;
      }

      if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
      if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
        return false;
      }
      if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
        return false;
      }
      if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
        return false;
      }
      pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
@@ -79,22 +79,22 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) {
    }

    case PT_GNU_EH_FRAME:
      if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
        return false;
      }
      eh_frame_offset_ = phdr.p_offset;
      if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
        return false;
      }
      eh_frame_size_ = phdr.p_memsz;
      break;

    case PT_DYNAMIC:
      if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
      if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
        return false;
      }
      dynamic_offset_ = phdr.p_offset;
      if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
      if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
        return false;
      }
      dynamic_size_ = phdr.p_memsz;
@@ -116,8 +116,8 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {
  ShdrType shdr;
  if (ehdr.e_shstrndx < ehdr.e_shnum) {
    uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
    if (memory_->Read(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
        && memory_->Read(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
    if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
        memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
      sec_offset = shdr.sh_offset;
      sec_size = shdr.sh_size;
    }
@@ -125,27 +125,27 @@ bool ElfInterface::ReadSectionHeaders(const EhdrType& ehdr) {

  // Skip the first header, it's always going to be NULL.
  for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
    if (!memory_->Read(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
    if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
      return false;
    }

    if (shdr.sh_type == SHT_PROGBITS) {
      // Look for the .debug_frame and .gnu_debugdata.
      if (!memory_->Read(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
      if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
        return false;
      }
      if (shdr.sh_name < sec_size) {
        std::string name;
        if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
          if (name == ".debug_frame") {
            if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
                && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
            if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
                memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
              debug_frame_offset_ = shdr.sh_offset;
              debug_frame_size_ = shdr.sh_size;
            }
          } else if (name == ".gnu_debugdata") {
            if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
                && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
            if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
                memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
              gnu_debugdata_offset_ = shdr.sh_offset;
              gnu_debugdata_size_ = shdr.sh_size;
            }
+7 −5
Original line number Diff line number Diff line
@@ -85,10 +85,10 @@ bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
  }

  Elf32_Phdr phdr;
  if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
  if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
    return true;
  }
  if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
  if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
    return true;
  }
  // The load_bias_ should always be set by this time.
@@ -98,13 +98,15 @@ bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
}

bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
  return StepExidx(pc, regs, process_memory) ||
      ElfInterface32::Step(pc, regs, process_memory);
  // Dwarf unwind information is precise about whether a pc is covered or not,
  // but arm unwind information only has ranges of pc. In order to avoid
  // incorrectly doing a bad unwind using arm unwind information for a
  // different function, always try and unwind with the dwarf information first.
  return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory);
}

bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
  RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
  // First try arm, then try dwarf.
  uint64_t entry_offset;
  if (!FindEntry(pc, &entry_offset)) {
    return false;
+49 −10
Original line number Diff line number Diff line
@@ -96,10 +96,16 @@ bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t

  offset_ = offset & (getpagesize() - 1);
  uint64_t aligned_offset = offset & ~(getpagesize() - 1);
  if (aligned_offset > static_cast<uint64_t>(buf.st_size) ||
      offset > static_cast<uint64_t>(buf.st_size)) {
    return false;
  }

  size_ = buf.st_size - aligned_offset;
  if (size < (UINT64_MAX - offset_) && size + offset_ < size_) {
  uint64_t max_size;
  if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) {
    // Truncate the mapped size.
    size_ = size + offset_;
    size_ = max_size;
  }
  void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
  if (map == MAP_FAILED) {
@@ -113,14 +119,15 @@ bool MemoryFileAtOffset::Init(const std::string& file, uint64_t offset, uint64_t
}

bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
  if (addr + size > size_) {
  uint64_t max_size;
  if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) {
    return false;
  }
  memcpy(dst, &data_[addr], size);
  return true;
}

static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
bool MemoryRemote::PtraceRead(uint64_t addr, long* value) {
#if !defined(__LP64__)
  // Cannot read an address greater than 32 bits.
  if (addr > UINT32_MAX) {
@@ -130,7 +137,7 @@ static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
  // ptrace() returns -1 and sets errno when the operation fails.
  // To disambiguate -1 from a valid result, we clear errno beforehand.
  errno = 0;
  *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
  *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr);
  if (*value == -1 && errno) {
    return false;
  }
@@ -138,11 +145,17 @@ static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
}

bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
  // Make sure that there is no overflow.
  uint64_t max_size;
  if (__builtin_add_overflow(addr, bytes, &max_size)) {
    return false;
  }

  size_t bytes_read = 0;
  long data;
  size_t align_bytes = addr & (sizeof(long) - 1);
  if (align_bytes != 0) {
    if (!PtraceRead(pid_, addr & ~(sizeof(long) - 1), &data)) {
    if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) {
      return false;
    }
    size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
@@ -154,7 +167,7 @@ bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
  }

  for (size_t i = 0; i < bytes / sizeof(long); i++) {
    if (!PtraceRead(pid_, addr, &data)) {
    if (!PtraceRead(addr, &data)) {
      return false;
    }
    memcpy(dst, &data, sizeof(long));
@@ -165,7 +178,7 @@ bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {

  size_t left_over = bytes & (sizeof(long) - 1);
  if (left_over) {
    if (!PtraceRead(pid_, addr, &data)) {
    if (!PtraceRead(addr, &data)) {
      return false;
    }
    memcpy(dst, &data, left_over);
@@ -175,7 +188,13 @@ bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
}

bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
  // The process_vm_readv call does will not always work on remote
  // Make sure that there is no overflow.
  uint64_t max_size;
  if (__builtin_add_overflow(addr, size, &max_size)) {
    return false;
  }

  // The process_vm_readv call will not always work on remote
  // processes, so only use it for reads from the current pid.
  // Use this method to avoid crashes if an address is invalid since
  // unwind data could try to access any part of the address space.
@@ -208,9 +227,29 @@ bool MemoryOffline::Init(const std::string& file, uint64_t offset) {
}

bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
  if (addr < start_ || addr + size > start_ + offset_ + size_) {
  uint64_t max_size;
  if (__builtin_add_overflow(addr, size, &max_size)) {
    return false;
  }

  uint64_t real_size;
  if (__builtin_add_overflow(start_, offset_, &real_size) ||
      __builtin_add_overflow(real_size, size_, &real_size)) {
    return false;
  }

  if (addr < start_ || max_size > real_size) {
    return false;
  }
  memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
  return true;
}

bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
  uint64_t max_read;
  if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
    return false;
  }
  // The check above guarantees that addr + begin_ will not overflow.
  return memory_->Read(addr + begin_, dst, size);
}
+18 −10
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#ifndef _LIBUNWINDSTACK_MEMORY_H
#define _LIBUNWINDSTACK_MEMORY_H

#include <assert.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
@@ -33,9 +34,16 @@ class Memory {

  virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;

  inline bool Read(uint64_t addr, void* start, void* field, size_t size) {
    return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start),
                field, size);
  inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
    if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
      return false;
    }
    uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start);
    if (__builtin_add_overflow(addr, offset, &offset)) {
      return false;
    }
    // The read will check if offset + size overflows.
    return Read(offset, field, size);
  }

  inline bool Read32(uint64_t addr, uint32_t* dst) {
@@ -103,6 +111,9 @@ class MemoryRemote : public Memory {

  pid_t pid() { return pid_; }

 protected:
  virtual bool PtraceRead(uint64_t addr, long* value);

 private:
  pid_t pid_;
};
@@ -118,15 +129,12 @@ class MemoryLocal : public Memory {
class MemoryRange : public Memory {
 public:
  MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
      : memory_(memory), begin_(begin), length_(end - begin_) {}
      : memory_(memory), begin_(begin), length_(end - begin) {
    assert(end > begin);
  }
  virtual ~MemoryRange() { delete memory_; }

  inline bool Read(uint64_t addr, void* dst, size_t size) override {
    if (addr + size <= length_) {
      return memory_->Read(addr + begin_, dst, size);
    }
    return false;
  }
  bool Read(uint64_t addr, void* dst, size_t size) override;

 private:
  Memory* memory_;
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vector>

#include <gtest/gtest.h>

#include "Memory.h"

#include "LogFake.h"

class MemoryBufferTest : public ::testing::Test {
 protected:
  void SetUp() override {
    ResetLogs();
    memory_.reset(new MemoryBuffer);
  }
  std::unique_ptr<MemoryBuffer> memory_;
};

TEST_F(MemoryBufferTest, empty) {
  ASSERT_EQ(0U, memory_->Size());
  std::vector<uint8_t> buffer(1024);
  ASSERT_FALSE(memory_->Read(0, buffer.data(), 1));
  ASSERT_EQ(nullptr, memory_->GetPtr(0));
  ASSERT_EQ(nullptr, memory_->GetPtr(1));
}

TEST_F(MemoryBufferTest, write_read) {
  memory_->Resize(256);
  ASSERT_EQ(256U, memory_->Size());
  ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
  ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
  ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
  ASSERT_TRUE(memory_->GetPtr(256) == nullptr);

  uint8_t* data = memory_->GetPtr(0);
  for (size_t i = 0; i < memory_->Size(); i++) {
    data[i] = i;
  }

  std::vector<uint8_t> buffer(memory_->Size());
  ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size()));
  for (size_t i = 0; i < buffer.size(); i++) {
    ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i;
  }
}

TEST_F(MemoryBufferTest, read_failures) {
  memory_->Resize(100);
  std::vector<uint8_t> buffer(200);
  ASSERT_FALSE(memory_->Read(0, buffer.data(), 101));
  ASSERT_FALSE(memory_->Read(100, buffer.data(), 1));
  ASSERT_FALSE(memory_->Read(101, buffer.data(), 2));
  ASSERT_FALSE(memory_->Read(99, buffer.data(), 2));
  ASSERT_TRUE(memory_->Read(99, buffer.data(), 1));
}

TEST_F(MemoryBufferTest, read_failure_overflow) {
  memory_->Resize(100);
  std::vector<uint8_t> buffer(200);

  ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
}
Loading