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

Commit 3b9ba98e authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes Ied2ab927,I16245af7

* changes:
  Silently ignore duplicate heap entries
  Handle segfaults while walking native heap
parents 7da38d6c 583a2508
Loading
Loading
Loading
Loading
+47 −8
Original line number Diff line number Diff line
@@ -14,7 +14,10 @@
 * limitations under the License.
 */

#include <errno.h>
#include <inttypes.h>
#include <sys/mman.h>
#include <unistd.h>

#include <map>
#include <utility>
@@ -22,6 +25,7 @@
#include "Allocator.h"
#include "HeapWalker.h"
#include "LeakFolding.h"
#include "ScopedSignalHandler.h"
#include "log.h"

bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
@@ -37,18 +41,26 @@ bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
    return true;
  } else {
    Range overlap = inserted.first->first;
    if (overlap != range) {
      ALOGE("range %p-%p overlaps with existing range %p-%p",
          reinterpret_cast<void*>(begin),
          reinterpret_cast<void*>(end),
          reinterpret_cast<void*>(overlap.begin),
          reinterpret_cast<void*>(overlap.end));
    }
    return false;
  }
}

bool HeapWalker::IsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info) {
  if (ptr >= valid_allocations_range_.begin && ptr < valid_allocations_range_.end) {
    AllocationMap::iterator it = allocations_.find(Range{ptr, ptr + 1});
bool HeapWalker::WordContainsAllocationPtr(uintptr_t word_ptr, Range* range, AllocationInfo** info) {
  walking_ptr_ = word_ptr;
  // This access may segfault if the process under test has done something strange,
  // for example mprotect(PROT_NONE) on a native heap page.  If so, it will be
  // caught and handled by mmaping a zero page over the faulting page.
  uintptr_t value = *reinterpret_cast<uintptr_t*>(word_ptr);
  walking_ptr_ = 0;
  if (value >= valid_allocations_range_.begin && value < valid_allocations_range_.end) {
    AllocationMap::iterator it = allocations_.find(Range{value, value + 1});
    if (it != allocations_.end()) {
      *range = it->first;
      *info = &it->second;
@@ -135,3 +147,30 @@ bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit,

  return true;
}

static bool MapOverPage(void* addr) {
  const size_t page_size = sysconf(_SC_PAGE_SIZE);
  void *page = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & ~(page_size-1));

  void* ret = mmap(page, page_size, PROT_READ, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0);
  if (ret == MAP_FAILED) {
    ALOGE("failed to map page at %p: %s", page, strerror(errno));
    return false;
  }

  return true;
}

void HeapWalker::HandleSegFault(ScopedSignalHandler& handler, int signal, siginfo_t* si, void* /*uctx*/) {
  uintptr_t addr = reinterpret_cast<uintptr_t>(si->si_addr);
  if (addr != walking_ptr_) {
    handler.reset();
    return;
  }
  ALOGW("failed to read page at %p, signal %d", si->si_addr, signal);
  if (!MapOverPage(si->si_addr)) {
    handler.reset();
  }
}

ScopedSignalHandler::SignalFn ScopedSignalHandler::handler_;
+23 −3
Original line number Diff line number Diff line
@@ -17,9 +17,12 @@
#ifndef LIBMEMUNREACHABLE_HEAP_WALKER_H_
#define LIBMEMUNREACHABLE_HEAP_WALKER_H_

#include <signal.h>

#include "android-base/macros.h"

#include "Allocator.h"
#include "ScopedSignalHandler.h"
#include "Tarjan.h"

// A range [begin, end)
@@ -28,6 +31,12 @@ struct Range {
  uintptr_t end;

  size_t size() const { return end - begin; };
  bool operator==(const Range& other) const {
    return this->begin == other.begin && this->end == other.end;
  }
  bool operator!=(const Range& other) const {
    return !(*this == other);
  }
};

// Comparator for Ranges that returns equivalence for overlapping ranges
@@ -41,10 +50,17 @@ class HeapWalker {
 public:
  HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
    allocations_(allocator), allocation_bytes_(0),
	roots_(allocator), root_vals_(allocator) {
	roots_(allocator), root_vals_(allocator),
	segv_handler_(allocator), walking_ptr_(0) {
    valid_allocations_range_.end = 0;
    valid_allocations_range_.begin = ~valid_allocations_range_.end;

    segv_handler_.install(SIGSEGV,
        [=](ScopedSignalHandler& handler, int signal, siginfo_t* siginfo, void* uctx) {
          this->HandleSegFault(handler, signal, siginfo, uctx);
      });
  }

  ~HeapWalker() {}
  bool Allocation(uintptr_t begin, uintptr_t end);
  void Root(uintptr_t begin, uintptr_t end);
@@ -70,7 +86,8 @@ class HeapWalker {
 private:

  void RecurseRoot(const Range& root);
  bool IsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
  bool WordContainsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
  void HandleSegFault(ScopedSignalHandler&, int, siginfo_t*, void*);

  DISALLOW_COPY_AND_ASSIGN(HeapWalker);
  Allocator<HeapWalker> allocator_;
@@ -81,6 +98,9 @@ class HeapWalker {

  allocator::vector<Range> roots_;
  allocator::vector<uintptr_t> root_vals_;

  ScopedSignalHandler segv_handler_;
  uintptr_t walking_ptr_;
};

template<class F>
@@ -92,7 +112,7 @@ inline void HeapWalker::ForEachPtrInRange(const Range& range, F&& f) {
  for (uintptr_t i = begin; i < range.end; i += sizeof(uintptr_t)) {
    Range ref_range;
    AllocationInfo* ref_info;
    if (IsAllocationPtr(*reinterpret_cast<uintptr_t*>(i), &ref_range, &ref_info)) {
    if (WordContainsAllocationPtr(i, &ref_range, &ref_info)) {
      f(ref_range, ref_info);
    }
  }
+84 −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.
 */

#ifndef LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
#define LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_

#include <errno.h>
#include <signal.h>

#include <functional>

#include "android-base/macros.h"

#include "log.h"

class ScopedSignalHandler {
 public:
  using Fn = std::function<void(ScopedSignalHandler&, int, siginfo_t*, void*)>;

  ScopedSignalHandler(Allocator<Fn> allocator) : allocator_(allocator), signal_(-1) {}
  ~ScopedSignalHandler() {
    reset();
  }

  template <class F>
  void install(int signal, F&& f) {
    LOG_ALWAYS_FATAL_IF(signal_ != -1, "ScopedSignalHandler already installed");

    handler_ = SignalFn(std::allocator_arg, allocator_,
        [=](int signal, siginfo_t* si, void* uctx) {
          f(*this, signal, si, uctx);
        });

    struct sigaction act{};
    act.sa_sigaction = [](int signal, siginfo_t* si, void* uctx) {
      handler_(signal, si, uctx);
    };
    act.sa_flags = SA_SIGINFO;

    int ret = sigaction(signal, &act, &old_act_);
    if (ret < 0) {
      LOG_ALWAYS_FATAL("failed to install segfault handler: %s", strerror(errno));
    }

    signal_ = signal;
  }

  void reset() {
    if (signal_ != -1) {
      int ret = sigaction(signal_, &old_act_, NULL);
      if (ret < 0) {
        ALOGE("failed to uninstall segfault handler");
      }
      handler_ = SignalFn{};
      signal_ = -1;
    }
  }


 private:
  using SignalFn = std::function<void(int, siginfo_t*, void*)>;
  DISALLOW_COPY_AND_ASSIGN(ScopedSignalHandler);
  Allocator<Fn> allocator_;
  int signal_;
  struct sigaction old_act_;
  // TODO(ccross): to support multiple ScopedSignalHandlers handler_ would need
  // to be a static map of signals to handlers, but allocated with Allocator.
  static SignalFn handler_;
};

#endif // LIBMEMUNREACHABLE_SCOPED_SIGNAL_HANDLER_H_
+27 −0
Original line number Diff line number Diff line
@@ -14,6 +14,9 @@
 * limitations under the License.
 */

#include <sys/mman.h>
#include <unistd.h>

#include "HeapWalker.h"

#include <gtest/gtest.h>
@@ -172,3 +175,27 @@ TEST_F(HeapWalkerTest, cycle) {
  EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
  ASSERT_EQ(2U, leaked.size());
}

TEST_F(HeapWalkerTest, segv) {
  const size_t page_size = sysconf(_SC_PAGE_SIZE);
  void* buffer1 = mmap(NULL, page_size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
  ASSERT_NE(buffer1, nullptr);
  void* buffer2;

  buffer2 = &buffer1;

  HeapWalker heap_walker(heap_);
  heap_walker.Allocation(buffer_begin(buffer1), buffer_begin(buffer1)+page_size);
  heap_walker.Root(buffer_begin(buffer2), buffer_end(buffer2));

  ASSERT_EQ(true, heap_walker.DetectLeaks());

  allocator::vector<Range> leaked(heap_);
  size_t num_leaks = 0;
  size_t leaked_bytes = 0;
  ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));

  EXPECT_EQ(0U, num_leaks);
  EXPECT_EQ(0U, leaked_bytes);
  ASSERT_EQ(0U, leaked.size());
}