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

Commit 69b4dfe1 authored by Christopher Ferris's avatar Christopher Ferris Committed by android-build-merger
Browse files

Merge "Add overflow checks in Memory objects."

am: 856bccbd

Change-Id: Id9e9c65c724d1ce75cc3cefed06d14f5785d2259
parents d3d3dddd 856bccbd
Loading
Loading
Loading
Loading
+17 −17
Original line number Original line Diff line number Diff line
@@ -42,7 +42,7 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) {
  uint64_t offset = ehdr.e_phoff;
  uint64_t offset = ehdr.e_phoff;
  for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
  for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
    PhdrType phdr;
    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;
      return false;
    }
    }


@@ -54,20 +54,20 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr) {
    case PT_LOAD:
    case PT_LOAD:
    {
    {
      // Get the flags first, if this isn't an executable header, ignore it.
      // 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;
        return false;
      }
      }
      if ((phdr.p_flags & PF_X) == 0) {
      if ((phdr.p_flags & PF_X) == 0) {
        continue;
        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;
        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;
        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;
        return false;
      }
      }
      pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
      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:
    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;
        return false;
      }
      }
      eh_frame_offset_ = phdr.p_offset;
      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;
        return false;
      }
      }
      eh_frame_size_ = phdr.p_memsz;
      eh_frame_size_ = phdr.p_memsz;
      break;
      break;


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


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


  Elf32_Phdr phdr;
  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;
    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;
    return true;
  }
  }
  // The load_bias_ should always be set by this time.
  // 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) {
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
  return StepExidx(pc, regs, process_memory) ||
  // Dwarf unwind information is precise about whether a pc is covered or not,
      ElfInterface32::Step(pc, regs, process_memory);
  // 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) {
bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
  RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
  RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
  // First try arm, then try dwarf.
  uint64_t entry_offset;
  uint64_t entry_offset;
  if (!FindEntry(pc, &entry_offset)) {
  if (!FindEntry(pc, &entry_offset)) {
    return false;
    return false;
+49 −10
Original line number Original line 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);
  offset_ = offset & (getpagesize() - 1);
  uint64_t aligned_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;
  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.
    // Truncate the mapped size.
    size_ = size + offset_;
    size_ = max_size;
  }
  }
  void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
  void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
  if (map == MAP_FAILED) {
  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) {
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;
    return false;
  }
  }
  memcpy(dst, &data_[addr], size);
  memcpy(dst, &data_[addr], size);
  return true;
  return true;
}
}


static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
bool MemoryRemote::PtraceRead(uint64_t addr, long* value) {
#if !defined(__LP64__)
#if !defined(__LP64__)
  // Cannot read an address greater than 32 bits.
  // Cannot read an address greater than 32 bits.
  if (addr > UINT32_MAX) {
  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.
  // ptrace() returns -1 and sets errno when the operation fails.
  // To disambiguate -1 from a valid result, we clear errno beforehand.
  // To disambiguate -1 from a valid result, we clear errno beforehand.
  errno = 0;
  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) {
  if (*value == -1 && errno) {
    return false;
    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) {
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;
  size_t bytes_read = 0;
  long data;
  long data;
  size_t align_bytes = addr & (sizeof(long) - 1);
  size_t align_bytes = addr & (sizeof(long) - 1);
  if (align_bytes != 0) {
  if (align_bytes != 0) {
    if (!PtraceRead(pid_, addr & ~(sizeof(long) - 1), &data)) {
    if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) {
      return false;
      return false;
    }
    }
    size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
    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++) {
  for (size_t i = 0; i < bytes / sizeof(long); i++) {
    if (!PtraceRead(pid_, addr, &data)) {
    if (!PtraceRead(addr, &data)) {
      return false;
      return false;
    }
    }
    memcpy(dst, &data, sizeof(long));
    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);
  size_t left_over = bytes & (sizeof(long) - 1);
  if (left_over) {
  if (left_over) {
    if (!PtraceRead(pid_, addr, &data)) {
    if (!PtraceRead(addr, &data)) {
      return false;
      return false;
    }
    }
    memcpy(dst, &data, left_over);
    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) {
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.
  // processes, so only use it for reads from the current pid.
  // Use this method to avoid crashes if an address is invalid since
  // Use this method to avoid crashes if an address is invalid since
  // unwind data could try to access any part of the address space.
  // 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) {
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;
    return false;
  }
  }
  memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
  memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
  return true;
  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 Original line Diff line number Diff line
@@ -17,6 +17,7 @@
#ifndef _LIBUNWINDSTACK_MEMORY_H
#ifndef _LIBUNWINDSTACK_MEMORY_H
#define _LIBUNWINDSTACK_MEMORY_H
#define _LIBUNWINDSTACK_MEMORY_H


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


  virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
  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) {
  inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
    return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start),
    if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
                field, size);
      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) {
  inline bool Read32(uint64_t addr, uint32_t* dst) {
@@ -103,6 +111,9 @@ class MemoryRemote : public Memory {


  pid_t pid() { return pid_; }
  pid_t pid() { return pid_; }


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

 private:
 private:
  pid_t pid_;
  pid_t pid_;
};
};
@@ -118,15 +129,12 @@ class MemoryLocal : public Memory {
class MemoryRange : public Memory {
class MemoryRange : public Memory {
 public:
 public:
  MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
  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_; }
  virtual ~MemoryRange() { delete memory_; }


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


 private:
 private:
  Memory* memory_;
  Memory* memory_;
+77 −0
Original line number Original line 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