blob: a472d0277072a22239416eecab1cfd4c510c8a6b [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
7#include "debugd/src/error_utils.h"
8#include "debugd/src/sandboxed_process.h"
9
10#include <memory>
11#include <string>
12#include <utility>
13#include <vector>
14
15#include <base/files/file_util.h>
16#include <base/files/scoped_temp_dir.h>
17#include <base/files/file_path.h>
18#include <base/logging.h>
19#include <base/values.h>
20#include <base/json/json_reader.h>
21#include <base/strings/string_number_conversions.h>
22#include <base/strings/string_split.h>
23#include <base/strings/stringprintf.h>
24#include <brillo/errors/error_codes.h>
25#include <build/build_config.h>
26#include <build/buildflag.h>
27#include <chromeos/dbus/service_constants.h>
28#include <vboot/crossystem.h>
29
30using base::ListValue;
31
32namespace debugd {
33
34namespace {
35constexpr char kErrorPath[] = "org.chromium.debugd.RunProbeFunctionError";
36constexpr char kSandboxInfoDir[] = "/etc/runtime_probe/sandbox";
37constexpr char kBinary[] = "/usr/bin/runtime_probe_helper";
38constexpr char kRunAs[] = "runtime_probe";
39
40} // namespace
41
42bool ProbeTool::EvaluateProbeFunction(brillo::ErrorPtr* error,
43 const std::string& sandbox_info,
44 const std::string& probe_statement,
45 std::string* probe_result,
46 int32_t* exit_code) {
47 std::unique_ptr<brillo::Process> process;
48
49 // Details of sandboxing for probing should be centralized in a single
50 // directory. Sandboxing is mandatory when we don't allow debug features.
51 if (VbGetSystemPropertyInt("cros_debug") != 1) {
52 std::unique_ptr<SandboxedProcess> sandboxed_process(new SandboxedProcess());
53
54 const auto seccomp_path = base::FilePath{kSandboxInfoDir}.Append(
55 base::StringPrintf("%s-seccomp.policy", sandbox_info.c_str()));
56 const auto minijail_args_path = base::FilePath{kSandboxInfoDir}.Append(
57 base::StringPrintf("%s.args", sandbox_info.c_str()));
58
59 if (!base::PathExists(seccomp_path) ||
60 !base::PathExists(minijail_args_path)) {
61 DEBUGD_ADD_ERROR(error, kErrorPath,
62 "Sandbox info is missing for this architecture");
63 return false;
64 }
65
66 // Read and parse the arguments, use JSON to avoid quote escaping.
67 std::string minijail_args_str;
68 if (!base::ReadFileToString(base::FilePath(minijail_args_path),
69 &minijail_args_str)) {
70 DEBUGD_ADD_ERROR(error, kErrorPath, "Failed to load minijail arguments");
71 return false;
72 }
73
74 DLOG(INFO) << "minijail arguments : " << minijail_args_str;
75
76 // Parse the JSON formatted minijail arguments.
77 std::unique_ptr<ListValue> minijail_args =
78 ListValue::From(base::JSONReader().ReadToValue(minijail_args_str));
79
80 if (!minijail_args) {
81 DEBUGD_ADD_ERROR(error, kErrorPath,
82 "minijail args are not stored in list");
83 return false;
84 }
85 std::vector<std::string> parsed_args;
86
87 // The following is the general minijail set up for runtime_probe in debugd
88 // /dev/log needs to be bind mounted before any possible tmpfs mount on run
89 parsed_args.push_back("-G"); // Inherit all the supplementary groups
90
91 // Run the process inside the new VFS namespace. The root of VFS is mounted
92 // on /var/empty/
93 parsed_args.push_back("-P");
94 parsed_args.push_back("/mnt/empty");
95
96 parsed_args.push_back("-b"); // Bind mount rootfs
97 parsed_args.push_back("/"); // Bind mount rootfs
98 parsed_args.push_back("-b"); // Bind mount /proc
99 parsed_args.push_back("/proc"); // Bind mount /proc
100 parsed_args.push_back("-b"); // Enable logging in minijail
101 parsed_args.push_back("/dev/log"); // Enable logging in minijail
102 parsed_args.push_back("-t"); // Bind mount /tmp
103 parsed_args.push_back("-r"); // Remount /proc read-only
104 parsed_args.push_back("-d"); // Mount a new /dev with minimum nodes
105
106 std::string tmp_str;
107 for (size_t i = 0; i < minijail_args->GetSize(); ++i) {
108 if (!minijail_args->GetString(i, &tmp_str)) {
109 DEBUGD_ADD_ERROR(error, kErrorPath,
110 "Failed to parse minijail arguments");
111 return false;
112 }
113 parsed_args.push_back(tmp_str);
114 }
115
116 sandboxed_process->SandboxAs(kRunAs, kRunAs);
117 sandboxed_process->SetSeccompFilterPolicyFile(seccomp_path.MaybeAsASCII());
118 DLOG(INFO) << "Sandbox for " << sandbox_info << " is ready";
119 if (!sandboxed_process->Init(parsed_args)) {
120 DEBUGD_ADD_ERROR(error, kErrorPath, "Process initialization failure.");
121 return false;
122 }
123 process = std::move(sandboxed_process);
124 } else {
125 process = std::make_unique<brillo::ProcessImpl>();
126 // Explicitly running it without sandboxing.
127 LOG(ERROR) << "Running " << sandbox_info << " without sandbox";
128 }
129
130 process->AddArg(kBinary);
131 process->AddArg(probe_statement);
132
133 base::ScopedTempDir temp_dir;
134 CHECK(temp_dir.CreateUniqueTempDir())
135 << "Fail to create unique temporary directory.";
136 std::string output_file = temp_dir.GetPath().Append(kRunAs).value();
137 process->RedirectOutput(output_file);
138 // TODO(itspeter): switch to Start() so to allow time consuming probe
139 // statement and possibility of multiprocesing. (b/129508946)
140 *exit_code = static_cast<int32_t>(process->Run());
141 base::ReadFileToString(base::FilePath(output_file), probe_result);
142 if (*exit_code) {
143 DEBUGD_ADD_ERROR(
144 error, kErrorPath,
145 base::StringPrintf("Helper returns non-zero (%d)", *exit_code));
146 return false;
147 }
148 return true;
149}
150
151} // namespace debugd