Merge "Fix google-explicit-constructor warnings."
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 47567c0..db5d0e0 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -351,21 +351,21 @@
         }
         if (a->op == OP_DOWNLOAD) {
             status = fb_download_data(transport, a->data, a->size);
-            status = a->func(a, status, status ? fb_get_error() : "");
+            status = a->func(a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_COMMAND) {
             status = fb_command(transport, a->cmd);
-            status = a->func(a, status, status ? fb_get_error() : "");
+            status = a->func(a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_QUERY) {
             status = fb_command_response(transport, a->cmd, resp);
-            status = a->func(a, status, status ? fb_get_error() : resp);
+            status = a->func(a, status, status ? fb_get_error().c_str() : resp);
             if (status) break;
         } else if (a->op == OP_NOTICE) {
             fprintf(stderr,"%s\n",(char*)a->data);
         } else if (a->op == OP_DOWNLOAD_SPARSE) {
             status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
-            status = a->func(a, status, status ? fb_get_error() : "");
+            status = a->func(a, status, status ? fb_get_error().c_str() : "");
             if (status) break;
         } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
             transport->WaitForDisconnect();
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index fa8f19a..94efcc3 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -49,6 +49,7 @@
 
 #include <android-base/parseint.h>
 #include <android-base/parsenetaddress.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
@@ -108,12 +109,9 @@
     {"vendor.img", "vendor.sig", "vendor", true},
 };
 
-static char* find_item(const char* item, const char* product) {
-    char *dir;
+static std::string find_item(const char* item, const char* product) {
     const char *fn;
-    char path[PATH_MAX + 128];
-
-    if(!strcmp(item,"boot")) {
+    if (!strcmp(item,"boot")) {
         fn = "boot.img";
     } else if(!strcmp(item,"recovery")) {
         fn = "recovery.img";
@@ -129,24 +127,22 @@
         fn = "android-info.txt";
     } else {
         fprintf(stderr,"unknown partition '%s'\n", item);
-        return 0;
+        return "";
     }
 
-    if(product) {
-        get_my_path(path);
-        sprintf(path + strlen(path),
-                "../../../target/product/%s/%s", product, fn);
-        return strdup(path);
+    if (product) {
+        std::string path = get_my_path();
+        path.erase(path.find_last_of('/'));
+        return android::base::StringPrintf("%s/../../../target/product/%s/%s",
+                                           path.c_str(), product, fn);
     }
 
-    dir = getenv("ANDROID_PRODUCT_OUT");
-    if((dir == 0) || (dir[0] == 0)) {
+    char* dir = getenv("ANDROID_PRODUCT_OUT");
+    if (dir == nullptr || dir[0] == '\0') {
         die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
-        return 0;
     }
 
-    sprintf(path, "%s/%s", dir, fn);
-    return strdup(path);
+    return android::base::StringPrintf("%s/%s", dir, fn);
 }
 
 static int64_t get_file_size(int fd) {
@@ -179,8 +175,8 @@
     return 0;
 }
 
-static void* load_file(const char* fn, int64_t* sz) {
-    int fd = open(fn, O_RDONLY | O_BINARY);
+static void* load_file(const std::string& path, int64_t* sz) {
+    int fd = open(path.c_str(), O_RDONLY | O_BINARY);
     if (fd == -1) return nullptr;
     return load_fd(fd, sz);
 }
@@ -964,18 +960,19 @@
     CloseArchive(zip);
 }
 
-static void do_send_signature(char* fn) {
-    char* xtn = strrchr(fn, '.');
-    if (!xtn) return;
+static void do_send_signature(const char* filename) {
+    if (android::base::EndsWith(filename, ".img") == false) {
+        return;
+    }
 
-    if (strcmp(xtn, ".img")) return;
-
-    strcpy(xtn, ".sig");
+    std::string sig_path = filename;
+    sig_path.erase(sig_path.size() - 4);
+    sig_path += ".sig";
 
     int64_t sz;
-    void* data = load_file(fn, &sz);
-    strcpy(xtn, ".img");
+    void* data = load_file(sig_path, &sz);
     if (data == nullptr) return;
+
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
 }
@@ -985,8 +982,8 @@
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
 
-    char* fname = find_item("info", product);
-    if (fname == nullptr) die("cannot find android-info.txt");
+    std::string fname = find_item("info", product);
+    if (fname.empty()) die("cannot find android-info.txt");
 
     int64_t sz;
     void* data = load_file(fname, &sz);
@@ -997,14 +994,14 @@
     for (size_t i = 0; i < ARRAY_SIZE(images); i++) {
         fname = find_item(images[i].part_name, product);
         fastboot_buffer buf;
-        if (load_buf(transport, fname, &buf)) {
+        if (load_buf(transport, fname.c_str(), &buf)) {
             if (images[i].is_optional)
                 continue;
             die("could not load %s\n", images[i].img_name);
         }
 
         auto flashall = [&](const std::string &partition) {
-            do_send_signature(fname);
+            do_send_signature(fname.c_str());
             if (erase_first && needs_erase(transport, partition.c_str())) {
                 fb_queue_erase(partition.c_str());
             }
@@ -1180,7 +1177,7 @@
         fprintf(stderr, "Erase successful, but not automatically formatting.\n");
         if (errMsg) fprintf(stderr, "%s", errMsg);
     }
-    fprintf(stderr,"FAILED (%s)\n", fb_get_error());
+    fprintf(stderr, "FAILED (%s)\n", fb_get_error().c_str());
 }
 
 int main(int argc, char **argv)
@@ -1441,8 +1438,8 @@
             fb_queue_download("boot.img", data, sz);
             fb_queue_command("boot", "booting");
         } else if(!strcmp(*argv, "flash")) {
-            char *pname = argv[1];
-            char *fname = 0;
+            char* pname = argv[1];
+            std::string fname;
             require(2);
             if (argc > 2) {
                 fname = argv[2];
@@ -1451,13 +1448,13 @@
                 fname = find_item(pname, product);
                 skip(2);
             }
-            if (fname == 0) die("cannot determine image filename for '%s'", pname);
+            if (fname.empty()) die("cannot determine image filename for '%s'", pname);
 
             auto flash = [&](const std::string &partition) {
                 if (erase_first && needs_erase(transport, partition.c_str())) {
                     fb_queue_erase(partition.c_str());
                 }
-                do_flash(transport, partition.c_str(), fname);
+                do_flash(transport, partition.c_str(), fname.c_str());
             };
             do_for_partitions(transport, pname, slot_override.c_str(), flash, true);
         } else if(!strcmp(*argv, "flash:raw")) {
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 1932bab..c2ea551 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -43,7 +43,7 @@
 int fb_command_response(Transport* transport, const char* cmd, char* response);
 int fb_download_data(Transport* transport, const void* data, uint32_t size);
 int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
-char *fb_get_error(void);
+const std::string fb_get_error();
 
 #define FB_COMMAND_SZ 64
 #define FB_RESPONSE_SZ 64
@@ -72,7 +72,7 @@
 char *mkmsg(const char *fmt, ...);
 __attribute__((__noreturn__)) void die(const char *fmt, ...);
 
-void get_my_path(char *path);
+std::string get_my_path();
 
 /* Current product */
 extern char cur_product[FB_RESPONSE_SZ + 1];
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index 1785b76..bfa83b0 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -36,16 +36,16 @@
 
 #include <algorithm>
 
+#include <android-base/stringprintf.h>
 #include <sparse/sparse.h>
 
 #include "fastboot.h"
 #include "transport.h"
 
-static char ERROR[128];
+static std::string g_error;
 
-char *fb_get_error(void)
-{
-    return ERROR;
+const std::string fb_get_error() {
+    return g_error;
 }
 
 static int check_response(Transport* transport, uint32_t size, char* response) {
@@ -54,14 +54,14 @@
     while (true) {
         int r = transport->Read(status, 64);
         if (r < 0) {
-            snprintf(ERROR, sizeof(ERROR), "status read failed (%s)", strerror(errno));
+            g_error = android::base::StringPrintf("status read failed (%s)", strerror(errno));
             transport->Close();
             return -1;
         }
         status[r] = 0;
 
         if (r < 4) {
-            snprintf(ERROR, sizeof(ERROR), "status malformed (%d bytes)", r);
+            g_error = android::base::StringPrintf("status malformed (%d bytes)", r);
             transport->Close();
             return -1;
         }
@@ -80,9 +80,9 @@
 
         if (!memcmp(status, "FAIL", 4)) {
             if (r > 4) {
-                snprintf(ERROR, sizeof(ERROR), "remote: %s", status + 4);
+                g_error = android::base::StringPrintf("remote: %s", status + 4);
             } else {
-                strcpy(ERROR, "remote failure");
+                g_error = "remote failure";
             }
             return -1;
         }
@@ -90,14 +90,14 @@
         if (!memcmp(status, "DATA", 4) && size > 0){
             uint32_t dsize = strtol(status + 4, 0, 16);
             if (dsize > size) {
-                strcpy(ERROR, "data size too large");
+                g_error = android::base::StringPrintf("data size too large (%d)", dsize);
                 transport->Close();
                 return -1;
             }
             return dsize;
         }
 
-        strcpy(ERROR,"unknown status code");
+        g_error = "unknown status code";
         transport->Close();
         break;
     }
@@ -108,7 +108,7 @@
 static int _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
     size_t cmdsize = strlen(cmd);
     if (cmdsize > 64) {
-        snprintf(ERROR, sizeof(ERROR), "command too large");
+        g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
         return -1;
     }
 
@@ -117,7 +117,7 @@
     }
 
     if (transport->Write(cmd, cmdsize) != static_cast<int>(cmdsize)) {
-        snprintf(ERROR, sizeof(ERROR), "command write failed (%s)", strerror(errno));
+        g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
         transport->Close();
         return -1;
     }
@@ -128,12 +128,12 @@
 static int _command_data(Transport* transport, const void* data, uint32_t size) {
     int r = transport->Write(data, size);
     if (r < 0) {
-        snprintf(ERROR, sizeof(ERROR), "data transfer failure (%s)", strerror(errno));
+        g_error = android::base::StringPrintf("data transfer failure (%s)", strerror(errno));
         transport->Close();
         return -1;
     }
     if (r != ((int) size)) {
-        snprintf(ERROR, sizeof(ERROR), "data transfer failure (short transfer)");
+        g_error = "data transfer failure (short transfer)";
         transport->Close();
         return -1;
     }
@@ -216,7 +216,7 @@
 
     if (len > TRANSPORT_BUF_SIZE) {
         if (transport_buf_len > 0) {
-            snprintf(ERROR, sizeof(ERROR), "internal error: transport_buf not empty\n");
+            g_error = "internal error: transport_buf not empty";
             return -1;
         }
         to_write = round_down(len, TRANSPORT_BUF_SIZE);
@@ -230,7 +230,7 @@
 
     if (len > 0) {
         if (len > TRANSPORT_BUF_SIZE) {
-            snprintf(ERROR, sizeof(ERROR), "internal error: too much left for transport_buf\n");
+            g_error = "internal error: too much left for transport_buf";
             return -1;
         }
         memcpy(transport_buf, ptr, len);
diff --git a/fastboot/util_linux.cpp b/fastboot/util_linux.cpp
index b788199..2c6aedb 100644
--- a/fastboot/util_linux.cpp
+++ b/fastboot/util_linux.cpp
@@ -28,27 +28,16 @@
 
 #include "fastboot.h"
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
 #include <unistd.h>
 #include <limits.h>
 
-void get_my_path(char *path)
-{
-    char proc[64];
-    char *x;
+#include <android-base/stringprintf.h>
 
-    sprintf(proc, "/proc/%d/exe", getpid());
-    int err = readlink(proc, path, PATH_MAX - 1);
-
-    if(err <= 0) {
-        path[0] = 0;
-    } else {
-        path[err] = 0;
-        x = strrchr(path,'/');
-        if(x) x[1] = 0;
-    }
+std::string get_my_path() {
+    std::string proc = android::base::StringPrintf("/proc/%d/exe", getpid());
+    char path[PATH_MAX + 1];
+    int rc = readlink(proc.c_str(), path, sizeof(path) - 1);
+    if (rc == -1) return "";
+    path[rc] = '\0';
+    return path;
 }
diff --git a/fastboot/util_osx.cpp b/fastboot/util_osx.cpp
index ae0b024..4bae7c4 100644
--- a/fastboot/util_osx.cpp
+++ b/fastboot/util_osx.cpp
@@ -31,19 +31,15 @@
 #import <Carbon/Carbon.h>
 #include <unistd.h>
 
-void get_my_path(char s[PATH_MAX])
-{
+std::string get_my_path() {
     CFBundleRef mainBundle = CFBundleGetMainBundle();
     CFURLRef executableURL = CFBundleCopyExecutableURL(mainBundle);
     CFStringRef executablePathString = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle);
     CFRelease(executableURL);
 
-    CFStringGetFileSystemRepresentation(executablePathString, s, PATH_MAX-1);
+    char path[PATH_MAX + 1];
+    CFStringGetFileSystemRepresentation(executablePathString, path, sizeof(PATH_MAX)-1);
     CFRelease(executablePathString);
 
-	char *x;
-    x = strrchr(s, '/');
-    if(x) x[1] = 0;
+    return path;
 }
-
-
diff --git a/fastboot/util_windows.cpp b/fastboot/util_windows.cpp
index ec52f39..3b22c55 100644
--- a/fastboot/util_windows.cpp
+++ b/fastboot/util_windows.cpp
@@ -38,14 +38,12 @@
 
 #include <windows.h>
 
-void get_my_path(char exe[PATH_MAX])
-{
-	char*  r;
+std::string get_my_path() {
+    char path[PATH_MAX + 1];
 
-	GetModuleFileName( NULL, exe, PATH_MAX-1 );
-	exe[PATH_MAX-1] = 0;
-	r = strrchr( exe, '\\' );
-	if (r)
-		*r = 0;
+    DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
+    if (result == 0 || result == sizeof(path) - 1) return "";
+    path[PATH_MAX - 1] = 0;
+
+    return path;
 }
-
diff --git a/init/devices.cpp b/init/devices.cpp
index e74140b..d452dd3 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -244,7 +244,11 @@
 
     mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
 
-    selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
+    if (selabel_lookup_best_match(sehandle, &secontext, path, links, mode)) {
+        ERROR("Device '%s' not created; cannot find SELinux label (%s)\n",
+                path, strerror(errno));
+        return;
+    }
     setfscreatecon(secontext);
 
     dev = makedev(major, minor);
@@ -254,14 +258,19 @@
      * racy. Fixing the gid race at least fixed the issue with system_server
      * opening dynamic input devices under the AID_INPUT gid. */
     setegid(gid);
-    mknod(path, mode, dev);
+    /* If the node already exists update its SELinux label to handle cases when
+     * it was created with the wrong context during coldboot procedure. */
+    if (mknod(path, mode, dev) && (errno == EEXIST)) {
+        if (lsetfilecon(path, secontext)) {
+            ERROR("Cannot set '%s' SELinux label on '%s' device (%s)\n",
+                    secontext, path, strerror(errno));
+        }
+    }
     chown(path, uid, -1);
     setegid(AID_ROOT);
 
-    if (secontext) {
-        freecon(secontext);
-        setfscreatecon(NULL);
-    }
+    freecon(secontext);
+    setfscreatecon(NULL);
 }
 
 static void add_platform_device(const char *path)
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
index 64d872a..3cb04cf 100644
--- a/liblog/event_tag_map.c
+++ b/liblog/event_tag_map.c
@@ -73,7 +73,7 @@
     if (newTagMap == NULL)
         return NULL;
 
-    fd = open(fileName, O_RDONLY);
+    fd = open(fileName, O_RDONLY | O_CLOEXEC);
     if (fd < 0) {
         fprintf(stderr, "%s: unable to open map '%s': %s\n",
             OUT_TAG, fileName, strerror(errno));
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index d844104..b894349 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -85,7 +85,7 @@
     .clear = logdClear,
     .getSize = logdGetSize,
     .setSize = logdSetSize,
-    .getReadableSize = logdGetSize,
+    .getReadableSize = logdGetReadableSize,
     .getPrune = logdGetPrune,
     .setPrune = logdSetPrune,
     .getStats = logdGetStats,
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 88afc14..59bd479 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -1043,7 +1043,7 @@
          * Anything in the Android Logger before the dmesg logging span will
          * be highly suspect regarding the monotonic time calculations.
          */
-        FILE *p = popen("/system/bin/dmesg", "r");
+        FILE *p = popen("/system/bin/dmesg", "re");
         if (p) {
             char *line = NULL;
             size_t len = 0;
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.c
index 5695e8a..f5e91c8 100644
--- a/liblog/pmsg_reader.c
+++ b/liblog/pmsg_reader.c
@@ -151,13 +151,13 @@
     memset(log_msg, 0, sizeof(*log_msg));
 
     if (transp->context.fd <= 0) {
-        int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY);
+        int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
 
         if (fd < 0) {
             return -errno;
         }
         if (fd == 0) { /* Argggg */
-            fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY);
+            fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
             close(0);
             if (fd < 0) {
                 return -errno;
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
index 9cd3c48..2ba31fa 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.c
@@ -54,7 +54,7 @@
 static int pmsgOpen()
 {
     if (pmsgLoggerWrite.context.fd < 0) {
-        pmsgLoggerWrite.context.fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+        pmsgLoggerWrite.context.fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
     }
 
     return pmsgLoggerWrite.context.fd;
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 19393ec..62366f2 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -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 @@
     return true;
   } else {
     Range overlap = inserted.first->first;
-    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));
+    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 @@
 
   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_;
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 7b851c4..3c1b513 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -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 @@
   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 @@
  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 @@
  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 @@
 
   allocator::vector<Range> roots_;
   allocator::vector<uintptr_t> root_vals_;
+
+  ScopedSignalHandler segv_handler_;
+  uintptr_t walking_ptr_;
 };
 
 template<class F>
@@ -92,7 +112,7 @@
   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);
     }
   }
diff --git a/libmemunreachable/ScopedSignalHandler.h b/libmemunreachable/ScopedSignalHandler.h
new file mode 100644
index 0000000..e006d43
--- /dev/null
+++ b/libmemunreachable/ScopedSignalHandler.h
@@ -0,0 +1,84 @@
+/*
+ * 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_
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index c3e1c4d..98e4aa1 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -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 @@
   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());
+}
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 997e682..920d504 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -810,12 +810,14 @@
     ASSERT_TRUE(NULL != (fp = popen(
       "( trap exit HUP QUIT INT PIPE KILL ; sleep 6; echo DONE )&"
       " logcat -b events -c 2>&1 ;"
+      " logcat -b events -g 2>&1 ;"
       " logcat -v brief -b events 2>&1",
       "r")));
 
     char buffer[BIG_BUFFER];
 
     int count = 0;
+    int minus_g = 0;
 
     int signals = 0;
 
@@ -833,6 +835,50 @@
             break;
         }
 
+        int size, consumed, max, payload;
+        char size_mult[3], consumed_mult[3];
+        size = consumed = max = payload = 0;
+        if (6 == sscanf(buffer, "events: ring buffer is %d%2s (%d%2s consumed),"
+                                " max entry is %db, max payload is %db",
+                                &size, size_mult, &consumed, consumed_mult,
+                                &max, &payload)) {
+            long full_size = size, full_consumed = consumed;
+
+            switch(size_mult[0]) {
+            case 'G':
+                full_size *= 1024;
+                /* FALLTHRU */
+            case 'M':
+                full_size *= 1024;
+                /* FALLTHRU */
+            case 'K':
+                full_size *= 1024;
+                /* FALLTHRU */
+            case 'b':
+                break;
+            }
+            switch(consumed_mult[0]) {
+            case 'G':
+                full_consumed *= 1024;
+                /* FALLTHRU */
+            case 'M':
+                full_consumed *= 1024;
+                /* FALLTHRU */
+            case 'K':
+                full_consumed *= 1024;
+                /* FALLTHRU */
+            case 'b':
+                break;
+            }
+            EXPECT_GT(full_size, full_consumed);
+            EXPECT_GT(full_size, max);
+            EXPECT_GT(max, payload);
+            EXPECT_GT(max, full_consumed);
+
+            ++minus_g;
+            continue;
+        }
+
         ++count;
 
         int p;
@@ -861,6 +907,7 @@
     pclose(fp);
 
     EXPECT_LE(1, count);
+    EXPECT_EQ(1, minus_g);
 
     EXPECT_EQ(1, signals);
 }
diff --git a/toolbox/upstream-netbsd/include/util.h b/toolbox/upstream-netbsd/include/util.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/toolbox/upstream-netbsd/include/util.h