blob: 43312849c0f365ad60d01c84a95ae879117a7c1b [file] [log] [blame]
Chun-Ta Lin32b8a512019-03-25 18:53:40 +08001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "debugd/src/probe_tool.h"
6
Clark Chungfb2997b2019-07-10 12:21:33 +08007#include <fcntl.h>
Chun-Ta Lin32b8a512019-03-25 18:53:40 +08008
Chung-Sheng Wu0d82b442020-10-13 11:37:24 +08009#include <array>
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080010#include <memory>
11#include <string>
12#include <utility>
13#include <vector>
14
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080015#include <base/files/file_path.h>
Clark Chungfb2997b2019-07-10 12:21:33 +080016#include <base/files/file_util.h>
17#include <base/files/scoped_file.h>
18#include <base/files/scoped_temp_dir.h>
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080019#include <base/json/json_reader.h>
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +080020#include <base/json/json_writer.h>
Clark Chungfb2997b2019-07-10 12:21:33 +080021#include <base/logging.h>
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +080022#include <base/optional.h>
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080023#include <base/strings/string_number_conversions.h>
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080024#include <base/strings/stringprintf.h>
Clark Chungfb2997b2019-07-10 12:21:33 +080025#include <base/strings/string_split.h>
26#include <base/values.h>
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080027#include <brillo/errors/error_codes.h>
28#include <build/build_config.h>
29#include <build/buildflag.h>
30#include <chromeos/dbus/service_constants.h>
31#include <vboot/crossystem.h>
32
Clark Chungfb2997b2019-07-10 12:21:33 +080033#include "debugd/src/error_utils.h"
34#include "debugd/src/sandboxed_process.h"
35
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080036namespace debugd {
37
38namespace {
39constexpr char kErrorPath[] = "org.chromium.debugd.RunProbeFunctionError";
40constexpr char kSandboxInfoDir[] = "/etc/runtime_probe/sandbox";
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +080041constexpr char kSandboxArgs[] = "/etc/runtime_probe/sandbox/args.json";
Chung-Sheng Wu0d82b442020-10-13 11:37:24 +080042constexpr std::array<const char*, 3> kBinaryAndArgs{"/usr/bin/runtime_probe",
43 "--helper", "--"};
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080044constexpr char kRunAs[] = "runtime_probe";
45
Tom Hughesd6c2d392020-08-24 18:12:11 -070046bool CreateNonblockingPipe(base::ScopedFD* read_fd, base::ScopedFD* write_fd) {
Clark Chungfb2997b2019-07-10 12:21:33 +080047 int pipe_fd[2];
48 int ret = pipe2(pipe_fd, O_CLOEXEC | O_NONBLOCK);
49 if (ret != 0) {
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +080050 PLOG(ERROR) << "Cannot create a pipe";
Clark Chungfb2997b2019-07-10 12:21:33 +080051 return false;
52 }
53 read_fd->reset(pipe_fd[0]);
54 write_fd->reset(pipe_fd[1]);
55 return true;
56}
57
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +080058std::string GetStringFromValue(const base::Value& value) {
59 std::string json;
60 base::JSONWriter::WriteWithOptions(
61 value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
62 return json;
63}
64
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +080065bool AppendSandboxArgs(brillo::ErrorPtr* error,
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +080066 const std::string& function_name,
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +080067 std::vector<std::string>* parsed_args) {
68 std::string minijail_args_str;
69 if (!base::ReadFileToString(base::FilePath(kSandboxArgs),
70 &minijail_args_str)) {
71 DEBUGD_ADD_ERROR_FMT(error, kErrorPath,
72 "Failed to read minijail arguments from: %s",
73 kSandboxArgs);
74 return false;
75 }
76 auto minijail_args_dict = base::JSONReader::Read(minijail_args_str);
77 if (!minijail_args_dict || !minijail_args_dict->is_dict()) {
78 DEBUGD_ADD_ERROR_FMT(error, kErrorPath,
79 "Minijail arguments are not stored in dict. Expected "
80 "dict but got: %s",
81 minijail_args_str.c_str());
82 return false;
83 }
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +080084 const auto* minijail_args = minijail_args_dict->FindListKey(function_name);
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +080085 if (!minijail_args) {
86 DEBUGD_ADD_ERROR_FMT(error, kErrorPath,
87 "Arguments of \"%s\" is not found in minijail "
88 "arguments file: %s",
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +080089 function_name.c_str(), kSandboxArgs);
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +080090 return false;
91 }
92 DVLOG(1) << "Minijail arguments: " << (*minijail_args);
93 for (const auto& arg : minijail_args->GetList()) {
94 if (!arg.is_string()) {
95 auto arg_str = GetStringFromValue(arg);
96 DEBUGD_ADD_ERROR_FMT(
97 error, kErrorPath,
98 "Failed to parse minijail arguments. Expected string but got: %s",
99 arg_str.c_str());
100 return false;
101 }
102 parsed_args->push_back(arg.GetString());
103 }
104 return true;
105}
106
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +0800107base::Optional<std::string> GetFunctionNameFromProbeStatement(
108 brillo::ErrorPtr* error, const std::string& probe_statement) {
109 // The name of the probe function is the only key in the dictionary.
110 auto probe_statement_dict = base::JSONReader::Read(probe_statement);
111 if (!probe_statement_dict || !probe_statement_dict->is_dict()) {
112 DEBUGD_ADD_ERROR_FMT(
113 error, kErrorPath,
114 "Failed to parse probe statement. Expected json but got: %s",
115 probe_statement.c_str());
116 return base::nullopt;
117 }
118 if (probe_statement_dict->DictSize() != 1) {
119 DEBUGD_ADD_ERROR_FMT(
120 error, kErrorPath,
121 "Expected only one probe function in probe statement but got: %zu",
122 probe_statement_dict->DictSize());
123 return base::nullopt;
124 }
125 auto it = probe_statement_dict->DictItems().begin();
126 const auto& function_name = it->first;
127 return function_name;
128}
129
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800130std::unique_ptr<brillo::Process> CreateSandboxedProcess(
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +0800131 brillo::ErrorPtr* error, const std::string& probe_statement) {
132 const auto function_name =
133 GetFunctionNameFromProbeStatement(error, probe_statement);
134 if (!function_name)
135 return nullptr;
136
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800137 auto sandboxed_process = std::make_unique<SandboxedProcess>();
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800138 // The following is the general minijail set up for runtime_probe in debugd
139 // /dev/log needs to be bind mounted before any possible tmpfs mount on run
140 // See:
141 // minijail0 manpage (`man 1 minijail0` in cros\_sdk)
142 // https://chromium.googlesource.com/chromiumos/docs/+/master/sandboxing.md
143 std::vector<std::string> parsed_args{
144 "-G", // Inherit all the supplementary groups
145 "-P", "/mnt/empty", // Set /mnt/empty as the root fs using pivot_root
146 "-b", "/", // Bind mount rootfs
147 "-b", "/proc", // Bind mount /proc
148 "-b", "/dev/log", // Enable logging
149 "-t", // Mount a tmpfs on /tmp
150 "-r", // Remount /proc readonly
151 "-d" // Mount /dev with a minimal set of nodes.
152 };
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +0800153 if (!AppendSandboxArgs(error, *function_name, &parsed_args))
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +0800154 return nullptr;
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800155
156 sandboxed_process->SandboxAs(kRunAs, kRunAs);
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +0800157 const auto seccomp_path = base::FilePath{kSandboxInfoDir}.Append(
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +0800158 base::StringPrintf("%s-seccomp.policy", function_name->c_str()));
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +0800159 if (!base::PathExists(seccomp_path)) {
160 DEBUGD_ADD_ERROR_FMT(error, kErrorPath,
161 "Seccomp policy file of \"%s\" is not found at: %s",
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +0800162 function_name->c_str(), seccomp_path.value().c_str());
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +0800163 return nullptr;
164 }
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800165 sandboxed_process->SetSeccompFilterPolicyFile(seccomp_path.MaybeAsASCII());
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +0800166 DVLOG(1) << "Sandbox for " << (*function_name) << " is ready";
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800167 if (!sandboxed_process->Init(parsed_args)) {
168 DEBUGD_ADD_ERROR(error, kErrorPath,
169 "Sandboxed process initialization failure");
170 return nullptr;
171 }
172 return sandboxed_process;
173}
174
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800175} // namespace
176
Clark Chungfb2997b2019-07-10 12:21:33 +0800177bool ProbeTool::EvaluateProbeFunction(
178 brillo::ErrorPtr* error,
Clark Chungfb2997b2019-07-10 12:21:33 +0800179 const std::string& probe_statement,
Tom Hughesd6c2d392020-08-24 18:12:11 -0700180 brillo::dbus_utils::FileDescriptor* outfd) {
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800181 // Details of sandboxing for probing should be centralized in a single
182 // directory. Sandboxing is mandatory when we don't allow debug features.
Chung-Sheng Wueb6d53a2020-11-24 14:26:08 +0800183 auto process = CreateSandboxedProcess(error, probe_statement);
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800184 if (process == nullptr)
185 return false;
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800186
Clark Chungfb2997b2019-07-10 12:21:33 +0800187 base::ScopedFD read_fd, write_fd;
188 if (!CreateNonblockingPipe(&read_fd, &write_fd)) {
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800189 DEBUGD_ADD_ERROR(error, kErrorPath, "Cannot create a pipe");
Clark Chungfb2997b2019-07-10 12:21:33 +0800190 return false;
191 }
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800192
Chung-Sheng Wu0d82b442020-10-13 11:37:24 +0800193 for (auto arg : kBinaryAndArgs) {
194 process->AddArg(arg);
195 }
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800196 process->AddArg(probe_statement);
Clark Chungfb2997b2019-07-10 12:21:33 +0800197 process->BindFd(write_fd.get(), STDOUT_FILENO);
198 process->Start();
199 process->Release();
200 *outfd = std::move(read_fd);
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800201 return true;
202}
203
204} // namespace debugd