blob: f72ae6d416b36919b33360e90d018cd0856aff69 [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 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
65std::unique_ptr<brillo::Process> CreateSandboxedProcess(
66 brillo::ErrorPtr* error,
67 const std::string& sandbox_info,
68 const std::string& probe_statement) {
69 auto sandboxed_process = std::make_unique<SandboxedProcess>();
70
71 const auto seccomp_path = base::FilePath{kSandboxInfoDir}.Append(
72 base::StringPrintf("%s-seccomp.policy", sandbox_info.c_str()));
73 const auto minijail_args_path = base::FilePath{kSandboxInfoDir}.Append(
74 base::StringPrintf("%s.args", sandbox_info.c_str()));
75
76 if (!base::PathExists(seccomp_path)) {
77 DEBUGD_ADD_ERROR_FMT(error, kErrorPath,
78 "Seccomp policy file of \"%s\" is not found at: %s",
79 sandbox_info.c_str(), seccomp_path.value().c_str());
80 return nullptr;
81 }
82
83 // Read and parse the arguments, use JSON to avoid quote escaping.
84 std::string minijail_args_str;
85 if (!base::ReadFileToString(base::FilePath(minijail_args_path),
86 &minijail_args_str)) {
87 DEBUGD_ADD_ERROR_FMT(error, kErrorPath,
88 "Failed to read minijail arguments from: %s",
89 minijail_args_path.value().c_str());
90 return nullptr;
91 }
92
93 DVLOG(1) << "minijail arguments: " << minijail_args_str;
94
95 // Parse the JSON-formatted minijail arguments.
96 auto minijail_args = base::JSONReader::Read(minijail_args_str);
97
98 if (!minijail_args) {
99 DEBUGD_ADD_ERROR(error, kErrorPath, "minijail args are not stored in list");
100 return nullptr;
101 }
102 // The following is the general minijail set up for runtime_probe in debugd
103 // /dev/log needs to be bind mounted before any possible tmpfs mount on run
104 // See:
105 // minijail0 manpage (`man 1 minijail0` in cros\_sdk)
106 // https://chromium.googlesource.com/chromiumos/docs/+/master/sandboxing.md
107 std::vector<std::string> parsed_args{
108 "-G", // Inherit all the supplementary groups
109 "-P", "/mnt/empty", // Set /mnt/empty as the root fs using pivot_root
110 "-b", "/", // Bind mount rootfs
111 "-b", "/proc", // Bind mount /proc
112 "-b", "/dev/log", // Enable logging
113 "-t", // Mount a tmpfs on /tmp
114 "-r", // Remount /proc readonly
115 "-d" // Mount /dev with a minimal set of nodes.
116 };
117
118 for (const auto& arg : minijail_args->GetList()) {
119 if (!arg.is_string()) {
120 auto arg_str = GetStringFromValue(arg);
121 DEBUGD_ADD_ERROR_FMT(
122 error, kErrorPath,
123 "Failed to parse minijail arguments. Expected string but got: %s",
124 arg_str.c_str());
125 return nullptr;
126 }
127 parsed_args.push_back(arg.GetString());
128 }
129
130 sandboxed_process->SandboxAs(kRunAs, kRunAs);
131 sandboxed_process->SetSeccompFilterPolicyFile(seccomp_path.MaybeAsASCII());
132 DVLOG(1) << "Sandbox for " << sandbox_info << " is ready";
133 if (!sandboxed_process->Init(parsed_args)) {
134 DEBUGD_ADD_ERROR(error, kErrorPath,
135 "Sandboxed process initialization failure");
136 return nullptr;
137 }
138 return sandboxed_process;
139}
140
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800141} // namespace
142
Clark Chungfb2997b2019-07-10 12:21:33 +0800143bool ProbeTool::EvaluateProbeFunction(
144 brillo::ErrorPtr* error,
145 const std::string& sandbox_info,
146 const std::string& probe_statement,
Tom Hughesd6c2d392020-08-24 18:12:11 -0700147 brillo::dbus_utils::FileDescriptor* outfd) {
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800148 // Details of sandboxing for probing should be centralized in a single
149 // directory. Sandboxing is mandatory when we don't allow debug features.
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800150 auto process = CreateSandboxedProcess(error, sandbox_info, probe_statement);
151 if (process == nullptr)
152 return false;
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800153
Clark Chungfb2997b2019-07-10 12:21:33 +0800154 base::ScopedFD read_fd, write_fd;
155 if (!CreateNonblockingPipe(&read_fd, &write_fd)) {
Chung-Sheng Wua0e3e242020-10-15 12:38:04 +0800156 DEBUGD_ADD_ERROR(error, kErrorPath, "Cannot create a pipe");
Clark Chungfb2997b2019-07-10 12:21:33 +0800157 return false;
158 }
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800159
Chung-Sheng Wu0d82b442020-10-13 11:37:24 +0800160 for (auto arg : kBinaryAndArgs) {
161 process->AddArg(arg);
162 }
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800163 process->AddArg(probe_statement);
Clark Chungfb2997b2019-07-10 12:21:33 +0800164 process->BindFd(write_fd.get(), STDOUT_FILENO);
165 process->Start();
166 process->Release();
167 *outfd = std::move(read_fd);
Chun-Ta Lin32b8a512019-03-25 18:53:40 +0800168 return true;
169}
170
171} // namespace debugd