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

Commit 5459a4d8 authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Refactor is_permissive_mte and remove allocs.

I accidentally modified is_permissive_mte to use a std::string
that allocates in a signal handler. Replace that call with a call
that reads the cmdline and doesn't do any allocations. Also,
refactor the code a bit to check values as you go instead of in
one big statement.

NOTE: This used to use /proc/XX/exe as the name of the executable,
      but this cl changes it to use the first argument in
      /proc/XX/cmdline so that this can distinguish zygote forked
      processes properly.

The MTE tests will fail when is_permissive_mte is enabled since it
causes MTE failures to be ignored.

Test: MTE test fails when environment variable MTE_PERMISSIVE is set to true.
Test: MTE test fails when property persist.sys.mte.permissive is set to true.
Test: MTE test fails when property persist.device_config.memory_safety_native.permissive.default is set to true.
Test: MTE test fails when property persist.device_config.memory_safety_native.permissive.process.debuggerd_test64 is set to true.
Test: Created a very long directory such that the command line name is truncated
Test: but the MTE tests still fails when using that truncated name.
Change-Id: Idf8f82a565e2cba8ec8af1b09d44f6ff5a3184da
parent 70fc1b79
Loading
Loading
Loading
Loading
+50 −8
Original line number Diff line number Diff line
@@ -103,19 +103,61 @@ static bool property_parse_bool(const char* name) {
  return cookie;
}

// Avoid using any other libc/libbase functions in this function to avoid doing
// any allocations and to avoid calling any disallowed functions by accident.
static const char* get_command_no_alloc(char* command, const size_t length) {
  int fd = open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC);
  if (fd == -1) {
    async_safe_format_log(ANDROID_LOG_WARN, "libc", "Opening /proc/self/cmdline failed: %s",
                          strerrorname_np(errno));
    return nullptr;
  }
  // Force the buffer to be null terminated to avoid cases where the first
  // argument is longer than the total buffer. This might truncate the first
  // argument of the command-line, but it's still possible to use the
  // truncated name.
  command[length - 1] = '\0';
  ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, command, length - 1));
  close(fd);
  if (bytes <= 0) {
    async_safe_format_log(ANDROID_LOG_WARN, "libc", "/proc/self/cmdline read error: %s",
                          bytes == -1 ? strerrorname_np(errno) : "zero bytes read");
    return nullptr;
  }

  // Find the basename of the first argument in the command-line.
  const char* arg0 = strrchr(command, '/');
  return arg0 != nullptr ? &arg0[1] : command;
}

static bool is_permissive_mte() {
  // Environment variable for testing or local use from shell.
  // DO NOT REPLACE property_parse_bool with GetBoolProperty. That uses std::string which allocates,
  // so it is not async-safe, and this function gets used in a signal handler.
  char* permissive_env = getenv("MTE_PERMISSIVE");
  if (permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue) {
    return true;
  }

  if (property_parse_bool("persist.sys.mte.permissive") ||
      property_parse_bool("persist.device_config.memory_safety_native.permissive.default")) {
    return true;
  }

  // getprogrname() always returns nullptr in this context, so we need to read
  // the cmdline directly to get the name of the running program.
  // In addition, use /proc/self/cmdline instead of readlink of /proc/self/exe
  // so that any process forked from the zygote has the correct name.
  char command_buffer[256];
  const char* command = get_command_no_alloc(command_buffer, sizeof(command_buffer));
  if (command == nullptr) {
    return false;
  }

  char process_sysprop_name[512];
  async_safe_format_buffer(process_sysprop_name, sizeof(process_sysprop_name),
                           "persist.device_config.memory_safety_native.permissive.process.%s",
                           android::base::Basename(android::base::GetExecutablePath()).c_str());
  // DO NOT REPLACE this with GetBoolProperty. That uses std::string which allocates, so it is
  // not async-safe, and this function gets used in a signal handler.
  return property_parse_bool("persist.sys.mte.permissive") ||
         property_parse_bool("persist.device_config.memory_safety_native.permissive.default") ||
         property_parse_bool(process_sysprop_name) ||
         (permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue);
                           command);
  return property_parse_bool(process_sysprop_name);
}

static bool parse_uint_with_error_reporting(const char* s, const char* name, int* v) {