adb: switch daemon_service_to_fd to string_view.
Test: test_adb.py
Test: test_device.py
Test: $ANDROID_HOST_OUT/nativetest64/adb_test/adb_test
Test: adb shell /data/nativetest64/adbd_test/adbd_test
Change-Id: I298517b688650c9d94bf837284e0264ca0ac6702
diff --git a/adb.h b/adb.h
index cdd6346..bd5d550 100644
--- a/adb.h
+++ b/adb.h
@@ -141,7 +141,7 @@
int service_to_fd(const char* name, atransport* transport);
#if !ADB_HOST
-unique_fd daemon_service_to_fd(const char* name, atransport* transport);
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport);
#endif
#if ADB_HOST
diff --git a/adb_utils.h b/adb_utils.h
index 6d12225..8712e1b 100644
--- a/adb_utils.h
+++ b/adb_utils.h
@@ -94,3 +94,16 @@
};
std::string GetLogFilePath();
+
+inline std::string_view StripTrailingNulls(std::string_view str) {
+ size_t n = 0;
+ for (auto it = str.rbegin(); it != str.rend(); ++it) {
+ if (*it != '\0') {
+ break;
+ }
+ ++n;
+ }
+
+ str.remove_suffix(n);
+ return str;
+}
diff --git a/daemon/services.cpp b/daemon/services.cpp
index 3182ddd..84a7655 100644
--- a/daemon/services.cpp
+++ b/daemon/services.cpp
@@ -151,14 +151,17 @@
kick_transport(t);
}
-unique_fd reverse_service(const char* command, atransport* transport) {
+unique_fd reverse_service(std::string_view command, atransport* transport) {
+ // TODO: Switch handle_forward_request to std::string_view.
+ std::string str(command);
+
int s[2];
if (adb_socketpair(s)) {
PLOG(ERROR) << "cannot create service socket pair.";
return unique_fd{};
}
VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
- if (!handle_forward_request(command, transport, s[1])) {
+ if (!handle_forward_request(str.c_str(), transport, s[1])) {
SendFail(s[1], "not a reverse forwarding command");
}
adb_close(s[1]);
@@ -167,15 +170,16 @@
// Shell service string can look like:
// shell[,arg1,arg2,...]:[command]
-unique_fd ShellService(const std::string& args, const atransport* transport) {
+unique_fd ShellService(std::string_view args, const atransport* transport) {
size_t delimiter_index = args.find(':');
if (delimiter_index == std::string::npos) {
LOG(ERROR) << "No ':' found in shell service arguments: " << args;
return unique_fd{};
}
- const std::string service_args = args.substr(0, delimiter_index);
- const std::string command = args.substr(delimiter_index + 1);
+ // TODO: android::base::Split(const std::string_view&, ...)
+ std::string service_args(args.substr(0, delimiter_index));
+ std::string command(args.substr(delimiter_index + 1));
// Defaults:
// PTY for interactive, raw for non-interactive.
@@ -192,15 +196,15 @@
type = SubprocessType::kPty;
} else if (arg == kShellServiceArgShellProtocol) {
protocol = SubprocessProtocol::kShell;
- } else if (android::base::StartsWith(arg, "TERM=")) {
- terminal_type = arg.substr(5);
+ } else if (arg.starts_with("TERM=")) {
+ terminal_type = arg.substr(strlen("TERM="));
} else if (!arg.empty()) {
// This is not an error to allow for future expansion.
LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
}
}
- return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
+ return StartSubprocess(command, terminal_type.c_str(), type, protocol);
}
static void spin_service(unique_fd fd) {
@@ -323,59 +327,77 @@
return nullptr;
}
-unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
- if (!strncmp("dev:", name, 4)) {
- return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
- } else if (!strncmp(name, "framebuffer:", 12)) {
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
+ // Historically, we received service names as a char*, and stopped at the first NUL byte.
+ // The client unintentionally sent strings with embedded NULs, which post-string_view, start
+ // being interpreted as part of the string, unless we explicitly strip them.
+ // Notably, shell checks that the part after "shell:" is empty to determine whether the session
+ // is interactive, and {'\0'} is non-empty.
+ name = StripTrailingNulls(name);
+
+ if (name.starts_with("dev:")) {
+ name.remove_prefix(strlen("dev:"));
+ return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
+ } else if (name.starts_with("framebuffer:")) {
return create_service_thread("fb", framebuffer_service);
- } else if (!strncmp(name, "jdwp:", 5)) {
- return create_jdwp_connection_fd(atoi(name + 5));
- } else if (!strncmp(name, "shell", 5)) {
- return ShellService(name + 5, transport);
- } else if (!strncmp(name, "exec:", 5)) {
- return StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
- } else if (!strncmp(name, "sync:", 5)) {
+ } else if (name.starts_with("jdwp:")) {
+ name.remove_prefix(strlen("jdwp:"));
+ std::string str(name);
+ return create_jdwp_connection_fd(atoi(str.c_str()));
+ } else if (name.starts_with("shell")) {
+ name.remove_prefix(strlen("shell"));
+ return ShellService(name, transport);
+ } else if (name.starts_with("exec:")) {
+ name.remove_prefix(strlen("exec:"));
+ return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
+ SubprocessProtocol::kNone);
+ } else if (name.starts_with("sync:")) {
return create_service_thread("sync", file_sync_service);
- } else if (!strncmp(name, "remount:", 8)) {
- std::string options(name + strlen("remount:"));
+ } else if (name.starts_with("remount:")) {
+ std::string arg(name.begin() + strlen("remount:"), name.end());
return create_service_thread("remount",
- std::bind(remount_service, std::placeholders::_1, options));
- } else if (!strncmp(name, "reboot:", 7)) {
- std::string arg(name + strlen("reboot:"));
+ std::bind(remount_service, std::placeholders::_1, arg));
+ } else if (name.starts_with("reboot:")) {
+ std::string arg(name.begin() + strlen("reboot:"), name.end());
return create_service_thread("reboot",
std::bind(reboot_service, std::placeholders::_1, arg));
- } else if (!strncmp(name, "root:", 5)) {
+ } else if (name.starts_with("root:")) {
return create_service_thread("root", restart_root_service);
- } else if (!strncmp(name, "unroot:", 7)) {
+ } else if (name.starts_with("unroot:")) {
return create_service_thread("unroot", restart_unroot_service);
- } else if (!strncmp(name, "backup:", 7)) {
- return StartSubprocess(
- android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
- nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
- } else if (!strncmp(name, "restore:", 8)) {
+ } else if (name.starts_with("backup:")) {
+ name.remove_prefix(strlen("backup:"));
+ std::string cmd = "/system/bin/bu backup ";
+ cmd += name;
+ return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+ } else if (name.starts_with("restore:")) {
return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
SubprocessProtocol::kNone);
- } else if (!strncmp(name, "tcpip:", 6)) {
+ } else if (name.starts_with("tcpip:")) {
+ name.remove_prefix(strlen("tcpip:"));
+ std::string str(name);
+
int port;
- if (sscanf(name + 6, "%d", &port) != 1) {
+ if (sscanf(str.c_str(), "%d", &port) != 1) {
return unique_fd{};
}
return create_service_thread("tcp",
std::bind(restart_tcp_service, std::placeholders::_1, port));
- } else if (!strncmp(name, "usb:", 4)) {
+ } else if (name.starts_with("usb:")) {
return create_service_thread("usb", restart_usb_service);
- } else if (!strncmp(name, "reverse:", 8)) {
- return reverse_service(name + 8, transport);
- } else if (!strncmp(name, "disable-verity:", 15)) {
+ } else if (name.starts_with("reverse:")) {
+ name.remove_prefix(strlen("reverse:"));
+ return reverse_service(name, transport);
+ } else if (name.starts_with("disable-verity:")) {
return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
std::placeholders::_1, false));
- } else if (!strncmp(name, "enable-verity:", 15)) {
+ } else if (name.starts_with("enable-verity:")) {
return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
std::placeholders::_1, true));
- } else if (!strcmp(name, "reconnect")) {
+ } else if (name == "reconnect") {
return create_service_thread(
"reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
- } else if (!strcmp(name, "spin")) {
+ } else if (name == "spin") {
return create_service_thread("spin", spin_service);
}
diff --git a/daemon/shell_service.cpp b/daemon/shell_service.cpp
index 8805fc1..595d5c6 100644
--- a/daemon/shell_service.cpp
+++ b/daemon/shell_service.cpp
@@ -140,8 +140,8 @@
class Subprocess {
public:
- Subprocess(const std::string& command, const char* terminal_type,
- SubprocessType type, SubprocessProtocol protocol);
+ Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol);
~Subprocess();
const std::string& command() const { return command_; }
@@ -191,9 +191,9 @@
DISALLOW_COPY_AND_ASSIGN(Subprocess);
};
-Subprocess::Subprocess(const std::string& command, const char* terminal_type,
- SubprocessType type, SubprocessProtocol protocol)
- : command_(command),
+Subprocess::Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+ SubprocessProtocol protocol)
+ : command_(std::move(command)),
terminal_type_(terminal_type ? terminal_type : ""),
type_(type),
protocol_(protocol) {
@@ -745,14 +745,13 @@
return read;
}
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
SubprocessProtocol protocol) {
D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
type == SubprocessType::kRaw ? "raw" : "PTY",
- protocol == SubprocessProtocol::kNone ? "none" : "shell",
- terminal_type, name);
+ protocol == SubprocessProtocol::kNone ? "none" : "shell", terminal_type, name.c_str());
- auto subprocess = std::make_unique<Subprocess>(name, terminal_type, type, protocol);
+ auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol);
if (!subprocess) {
LOG(ERROR) << "failed to allocate new subprocess";
return ReportError(protocol, "failed to allocate new subprocess");
diff --git a/daemon/shell_service.h b/daemon/shell_service.h
index 2a48923..421d61f 100644
--- a/daemon/shell_service.h
+++ b/daemon/shell_service.h
@@ -16,6 +16,8 @@
#pragma once
+#include <string>
+
#include "adb_unique_fd.h"
enum class SubprocessType {
@@ -32,5 +34,5 @@
// shell is started, otherwise |name| is executed non-interactively.
//
// Returns an open FD connected to the subprocess or -1 on failure.
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
SubprocessProtocol protocol);