Merge "Add support for disabling the greylist." into oc-dev
diff --git a/android-changes-for-ndk-developers.md b/android-changes-for-ndk-developers.md
index ebdae83..5279118 100644
--- a/android-changes-for-ndk-developers.md
+++ b/android-changes-for-ndk-developers.md
@@ -145,6 +145,12 @@
 temporarily support these libraries; so if you see a warning that means
 your code will not work in a future release -- please fix it now!
 
+In O and later, the system property `debug.ld.greylist_disabled` can be
+used to deny access to the greylist even to an app that would normally
+be allowed it. This allows you to test compatibility without bumping the
+app's `targetSdkVersion`. Use `setprop debug.ld.greylist_disabled true`
+to turn this on (any other value leaves the greylist enabled).
+
 ```
 $ readelf --dynamic libBroken.so | grep NEEDED
  0x00000001 (NEEDED)                     Shared library: [libnativehelper.so]
diff --git a/libc/private/CachedProperty.h b/libc/private/CachedProperty.h
new file mode 100644
index 0000000..0a41abf
--- /dev/null
+++ b/libc/private/CachedProperty.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <string.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#include "private/bionic_lock.h"
+
+class CachedProperty {
+ public:
+  CachedProperty(const char* property_name)
+    : property_name_(property_name),
+      prop_info_(nullptr),
+      cached_area_serial_(0),
+      cached_property_serial_(0) {
+    cached_value_[0] = '\0';
+  }
+
+  const char* Get() {
+    lock_.lock();
+
+    // Do we have a `struct prop_info` yet?
+    if (prop_info_ == nullptr) {
+      // `__system_property_find` is expensive, so only retry if a property
+      // has been created since last time we checked.
+      uint32_t property_area_serial = __system_property_area_serial();
+      if (property_area_serial != cached_area_serial_) {
+        prop_info_ = __system_property_find(property_name_);
+        cached_area_serial_ = property_area_serial;
+      }
+    }
+
+    if (prop_info_ != nullptr) {
+      // Only bother re-reading the property if it's actually changed since last time.
+      uint32_t property_serial = __system_property_serial(prop_info_);
+      if (property_serial != cached_property_serial_) {
+        __system_property_read_callback(prop_info_, &CachedProperty::Callback, this);
+      }
+    }
+
+    lock_.unlock();
+    return cached_value_;
+  }
+
+ private:
+  Lock lock_;
+  const char* property_name_;
+  const prop_info* prop_info_;
+  uint32_t cached_area_serial_;
+  uint32_t cached_property_serial_;
+  char cached_value_[PROP_VALUE_MAX];
+
+  static void Callback(void* data, const char*, const char* value, uint32_t serial) {
+    CachedProperty* instance = reinterpret_cast<CachedProperty*>(data);
+    instance->cached_property_serial_ = serial;
+    strcpy(instance->cached_value_, value);
+  }
+};
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 1647db7..2777d73 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -185,7 +185,7 @@
   };
 
   // If you're targeting N, you don't get the greylist.
-  if (get_application_target_sdk_version() >= __ANDROID_API_N__) {
+  if (g_greylist_disabled || get_application_target_sdk_version() >= __ANDROID_API_N__) {
     return false;
   }
 
diff --git a/linker/linker_logger.cpp b/linker/linker_logger.cpp
index b2ea320..717667c 100644
--- a/linker/linker_logger.cpp
+++ b/linker/linker_logger.cpp
@@ -26,32 +26,21 @@
  * SUCH DAMAGE.
  */
 
+#include "linker_logger.h"
+
 #include <string.h>
 #include <sys/prctl.h>
-#include <sys/system_properties.h>
 #include <unistd.h>
 
 #include <string>
 #include <vector>
 
 #include "android-base/strings.h"
-#include "linker_logger.h"
+#include "private/CachedProperty.h"
 #include "private/libc_logging.h"
 
 LinkerLogger g_linker_logger;
-
-static const char* kSystemLdDebugProperty = "debug.ld.all";
-static const char* kLdDebugPropertyPrefix = "debug.ld.app.";
-
-static const char* kOptionErrors = "dlerror";
-static const char* kOptionDlopen = "dlopen";
-static const char* kOptionDlsym = "dlsym";
-
-static std::string property_get(const char* name) {
-  char value[PROP_VALUE_MAX] = {};
-  __system_property_get(name, value);
-  return value;
-}
+bool g_greylist_disabled = false;
 
 static uint32_t ParseProperty(const std::string& value) {
   if (value.empty()) {
@@ -63,38 +52,22 @@
   uint32_t flags = 0;
 
   for (const auto& o : options) {
-    if (o == kOptionErrors) {
+    if (o == "dlerror") {
       flags |= kLogErrors;
-    } else if (o == kOptionDlopen){
+    } else if (o == "dlopen") {
       flags |= kLogDlopen;
-    } else if (o == kOptionDlsym){
+    } else if (o == "dlsym") {
       flags |= kLogDlsym;
     } else {
-      __libc_format_log(ANDROID_LOG_WARN, "linker", "Unknown debug.ld option \"%s\", will ignore.", o.c_str());
+      __libc_format_log(ANDROID_LOG_WARN, "linker", "Ignoring unknown debug.ld option \"%s\"",
+                        o.c_str());
     }
   }
 
   return flags;
 }
 
-void LinkerLogger::ResetState() {
-  // the most likely scenario app is not debuggable and
-  // is running on user build - the logging is disabled.
-  if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) {
-    return;
-  }
-
-  flags_ = 0;
-
-  // Check flag applied to all processes first.
-  std::string value = property_get(kSystemLdDebugProperty);
-  flags_ |= ParseProperty(value);
-
-  // Ignore processes started without argv (http://b/33276926).
-  if (g_argv[0] == nullptr) {
-    return;
-  }
-
+static void GetAppSpecificProperty(char* buffer) {
   // Get process basename.
   const char* process_name_start = basename(g_argv[0]);
 
@@ -106,10 +79,42 @@
                              std::string(process_name_start, (process_name_end - process_name_start)) :
                              std::string(process_name_start);
 
-  std::string property_name = std::string(kLdDebugPropertyPrefix) + process_name;
+  std::string property_name = std::string("debug.ld.app.") + process_name;
+  __system_property_get(property_name.c_str(), buffer);
+}
 
-  value = property_get(property_name.c_str());
-  flags_ |= ParseProperty(value);
+void LinkerLogger::ResetState() {
+  // The most likely scenario app is not debuggable and
+  // is running on a user build, in which case logging is disabled.
+  if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) {
+    return;
+  }
+
+  // This is a convenient place to check whether the greylist should be disabled for testing.
+  static CachedProperty greylist_disabled("debug.ld.greylist_disabled");
+  bool old_value = g_greylist_disabled;
+  g_greylist_disabled = (strcmp(greylist_disabled.Get(), "true") == 0);
+  if (g_greylist_disabled != old_value) {
+    __libc_format_log(ANDROID_LOG_INFO, "linker", "%s greylist",
+                      g_greylist_disabled ? "Disabling" : "Enabling");
+  }
+
+  flags_ = 0;
+
+  // For logging, check the flag applied to all processes first.
+  static CachedProperty debug_ld_all("debug.ld.all");
+  flags_ |= ParseProperty(debug_ld_all.Get());
+
+  // Ignore processes started without argv (http://b/33276926).
+  if (g_argv[0] == nullptr) {
+    return;
+  }
+
+  // Otherwise check the app-specific property too.
+  // We can't easily cache the property here because argv[0] changes.
+  char debug_ld_app[PROP_VALUE_MAX] = {};
+  GetAppSpecificProperty(debug_ld_app);
+  flags_ |= ParseProperty(debug_ld_app);
 }
 
 void LinkerLogger::Log(uint32_t type, const char* format, ...) {
@@ -122,4 +127,3 @@
   __libc_format_log_va_list(ANDROID_LOG_DEBUG, "linker", format, ap);
   va_end(ap);
 }
-
diff --git a/linker/linker_logger.h b/linker/linker_logger.h
index f37b974..3e53f74 100644
--- a/linker/linker_logger.h
+++ b/linker/linker_logger.h
@@ -58,4 +58,8 @@
 extern LinkerLogger g_linker_logger;
 extern char** g_argv;
 
+// If the system property debug.ld.greylist_disabled is true, we'll not use the greylist
+// regardless of API level.
+extern bool g_greylist_disabled;
+
 #endif /* _LINKER_LOGGER_H_ */