blob: d45badaddcf270f417319c70fd9e614134b13a9b [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>
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080022#include <base/strings/string_number_conversions.h>
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080023#include <base/strings/stringprintf.h>
Clark Chungfb2997b2019-07-10 12:21:33 +080024#include <base/strings/string_split.h>
25#include <base/values.h>
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080026#include <brillo/errors/error_codes.h>
27#include <build/build_config.h>
28#include <build/buildflag.h>
29#include <chromeos/dbus/service_constants.h>
30#include <vboot/crossystem.h>
31
Clark Chungfb2997b2019-07-10 12:21:33 +080032#include "debugd/src/error_utils.h"
33#include "debugd/src/sandboxed_process.h"
34
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080035using base::ListValue;
36
37namespace debugd {
38
39namespace {
40constexpr char kErrorPath[] = "org.chromium.debugd.RunProbeFunctionError";
41constexpr char kSandboxInfoDir[] = "/etc/runtime_probe/sandbox";
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +080042constexpr char kSandboxArgs[] = "/etc/runtime_probe/sandbox/args.json";
Chung-Sheng Wu0d82b442020-10-13 11:37:24 +080043constexpr std::array<const char*, 3> kBinaryAndArgs{"/usr/bin/runtime_probe",
44 "--helper", "--"};
Chun-Ta Lin32b8a512019-03-25 18:53:40 +080045constexpr char kRunAs[] = "runtime_probe";
46
Tom Hughesd6c2d392020-08-24 18:12:11 -070047bool CreateNonblockingPipe(base::ScopedFD* read_fd, base::ScopedFD* write_fd) {
Clark Chungfb2997b2019-07-10 12:21:33 +080048 int pipe_fd[2];
49 int ret = pipe2(pipe_fd, O_CLOEXEC | O_NONBLOCK);
50 if (ret != 0) {
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +080051 PLOG(ERROR) << "Cannot create a pipe";
Clark Chungfb2997b2019-07-10 12:21:33 +080052 return false;
53 }
54 read_fd->reset(pipe_fd[0]);
55 write_fd->reset(pipe_fd[1]);
56 return true;
57}
58
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +080059std::string GetStringFromValue(const base::Value& value) {
60 std::string json;
61 base::JSONWriter::WriteWithOptions(
62 value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
63 return json;
64}
65
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +080066bool AppendSandboxArgs(brillo::ErrorPtr* error,
67 const std::string& sandbox_info,
68 std::vector<std::string>* parsed_args) {
69 std::string minijail_args_str;
70 if (!base::ReadFileToString(base::FilePath(kSandboxArgs),
71 &minijail_args_str)) {
72 DEBUGD_ADD_ERROR_FMT(error, kErrorPath,
73 "Failed to read minijail arguments from: %s",
74 kSandboxArgs);
75 return false;
76 }
77 auto minijail_args_dict = base::JSONReader::Read(minijail_args_str);
78 if (!minijail_args_dict || !minijail_args_dict->is_dict()) {
79 DEBUGD_ADD_ERROR_FMT(error, kErrorPath,
80 "Minijail arguments are not stored in dict. Expected "
81 "dict but got: %s",
82 minijail_args_str.c_str());
83 return false;
84 }
85 const auto* minijail_args = minijail_args_dict->FindListKey(sandbox_info);
86 if (!minijail_args) {
87 DEBUGD_ADD_ERROR_FMT(error, kErrorPath,
88 "Arguments of \"%s\" is not found in minijail "
89 "arguments file: %s",
90 sandbox_info.c_str(), kSandboxArgs);
91 return false;
92 }
93 DVLOG(1) << "Minijail arguments: " << (*minijail_args);
94 for (const auto& arg : minijail_args->GetList()) {
95 if (!arg.is_string()) {
96 auto arg_str = GetStringFromValue(arg);
97 DEBUGD_ADD_ERROR_FMT(
98 error, kErrorPath,
99 "Failed to parse minijail arguments. Expected string but got: %s",
100 arg_str.c_str());
101 return false;
102 }
103 parsed_args->push_back(arg.GetString());
104 }
105 return true;
106}
107
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800108std::unique_ptr<brillo::Process> CreateSandboxedProcess(
109 brillo::ErrorPtr* error,
110 const std::string& sandbox_info,
111 const std::string& probe_statement) {
112 auto sandboxed_process = std::make_unique<SandboxedProcess>();
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800113 // The following is the general minijail set up for runtime_probe in debugd
114 // /dev/log needs to be bind mounted before any possible tmpfs mount on run
115 // See:
116 // minijail0 manpage (`man 1 minijail0` in cros\_sdk)
117 // https://chromium.googlesource.com/chromiumos/docs/+/master/sandboxing.md
118 std::vector<std::string> parsed_args{
119 "-G", // Inherit all the supplementary groups
120 "-P", "/mnt/empty", // Set /mnt/empty as the root fs using pivot_root
121 "-b", "/", // Bind mount rootfs
122 "-b", "/proc", // Bind mount /proc
123 "-b", "/dev/log", // Enable logging
124 "-t", // Mount a tmpfs on /tmp
125 "-r", // Remount /proc readonly
126 "-d" // Mount /dev with a minimal set of nodes.
127 };
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +0800128 if (!AppendSandboxArgs(error, sandbox_info, &parsed_args))
129 return nullptr;
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800130
131 sandboxed_process->SandboxAs(kRunAs, kRunAs);
Chung-Sheng Wu1e4d5d62020-11-05 14:53:39 +0800132 const auto seccomp_path = base::FilePath{kSandboxInfoDir}.Append(
133 base::StringPrintf("%s-seccomp.policy", sandbox_info.c_str()));
134 if (!base::PathExists(seccomp_path)) {
135 DEBUGD_ADD_ERROR_FMT(error, kErrorPath,
136 "Seccomp policy file of \"%s\" is not found at: %s",
137 sandbox_info.c_str(), seccomp_path.value().c_str());
138 return nullptr;
139 }
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800140 sandboxed_process->SetSeccompFilterPolicyFile(seccomp_path.MaybeAsASCII());
141 DVLOG(1) << "Sandbox for " << sandbox_info << " is ready";
142 if (!sandboxed_process->Init(parsed_args)) {
143 DEBUGD_ADD_ERROR(error, kErrorPath,
144 "Sandboxed process initialization failure");
145 return nullptr;
146 }
147 return sandboxed_process;
148}
149
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800150} // namespace
151
Clark Chungfb2997b2019-07-10 12:21:33 +0800152bool ProbeTool::EvaluateProbeFunction(
153 brillo::ErrorPtr* error,
154 const std::string& sandbox_info,
155 const std::string& probe_statement,
Tom Hughesd6c2d392020-08-24 18:12:11 -0700156 brillo::dbus_utils::FileDescriptor* outfd) {
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800157 // Details of sandboxing for probing should be centralized in a single
158 // directory. Sandboxing is mandatory when we don't allow debug features.
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800159 auto process = CreateSandboxedProcess(error, sandbox_info, probe_statement);
160 if (process == nullptr)
161 return false;
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800162
Clark Chungfb2997b2019-07-10 12:21:33 +0800163 base::ScopedFD read_fd, write_fd;
164 if (!CreateNonblockingPipe(&read_fd, &write_fd)) {
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800165 DEBUGD_ADD_ERROR(error, kErrorPath, "Cannot create a pipe");
Clark Chungfb2997b2019-07-10 12:21:33 +0800166 return false;
167 }
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800168
Chung-Sheng Wu0d82b442020-10-13 11:37:24 +0800169 for (auto arg : kBinaryAndArgs) {
170 process->AddArg(arg);
171 }
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800172 process->AddArg(probe_statement);
Clark Chungfb2997b2019-07-10 12:21:33 +0800173 process->BindFd(write_fd.get(), STDOUT_FILENO);
174 process->Start();
175 process->Release();
176 *outfd = std::move(read_fd);
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800177 return true;
178}
179
180} // namespace debugd