blob: fd78209537c8416446383ad004b466e9c040ddb5 [file] [log] [blame]
Mike Frysingerc0871522013-09-16 14:07:27 -04001// Copyright (c) 2013 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
Alex Vakulenko262be3f2014-07-30 15:25:50 -07005#include "debugd/src/crash_sender_tool.h"
Mike Frysingerc0871522013-09-16 14:07:27 -04006
Ian Barkley-Yeung7b7b0352019-04-23 12:52:04 -07007#include <fcntl.h>
8#include <sys/stat.h>
9#include <sys/types.h>
10#include <unistd.h>
11
12#include <memory>
13
14#include <base/files/file.h>
15#include <base/files/file_util.h>
16#include <base/files/scoped_temp_dir.h>
17#include <base/strings/string_number_conversions.h>
Ian Barkley-Yeungc68cfc32019-07-15 16:43:57 -070018#include <brillo/dbus/exported_property_set.h>
Ian Barkley-Yeung7b7b0352019-04-23 12:52:04 -070019
20#include "debugd/src/error_utils.h"
Alex Vakulenko262be3f2014-07-30 15:25:50 -070021#include "debugd/src/process_with_id.h"
Mike Frysingerc0871522013-09-16 14:07:27 -040022
23namespace debugd {
Ian Barkley-Yeung7b7b0352019-04-23 12:52:04 -070024namespace {
25constexpr char kErrorIOError[] = "org.chromium.debugd.error.IOError";
26} // namespace
27
28constexpr char CrashSenderTool::kErrorBadFileName[];
Mike Frysingerc0871522013-09-16 14:07:27 -040029
Eric Carusoc93a15c2017-04-24 16:15:12 -070030void CrashSenderTool::UploadCrashes() {
Ian Barkley-Yeung7b7b0352019-04-23 12:52:04 -070031 RunCrashSender(false /* ignore_hold_off_time */,
32 base::FilePath("") /* crash_directory */);
33}
34
35bool CrashSenderTool::UploadSingleCrash(
36 const std::vector<std::tuple<std::string, base::ScopedFD>>& in_files,
37 brillo::ErrorPtr* error) {
38 // debugd runs in a non-root mount namespace and mounts a new tmpfs on /tmp
39 // inside the namespace, so this should be invisible to all other processes
40 // and not written to disk.
41 //
42 // *It is a privacy violation* if these files are visible to non-root
43 // processes or are written unencrypted to disk!
44 base::FilePath crash_directory("/tmp/crash");
Qijiang Fan26fd9222020-03-31 20:16:40 +090045 crash_directory = crash_directory.AddExtension(
46 base::NumberToString(next_crash_directory_id_));
Ian Barkley-Yeung7b7b0352019-04-23 12:52:04 -070047 next_crash_directory_id_++;
48
49 // We need to be sure to clean up the tmp directory to avoid leaking
50 // resources.
51 base::ScopedTempDir crash_directory_holder;
52 if (!crash_directory_holder.Set(crash_directory)) {
53 DEBUGD_ADD_ERROR(error, kErrorIOError, "Create directory failed");
54 return false;
55 }
56
57 for (const auto& in_file_tuple : in_files) {
58 base::FilePath file_name(std::get<0>(in_file_tuple));
59 const base::ScopedFD& file_descriptor = std::get<1>(in_file_tuple);
60
61 // Sanitize file names to ensure a bad actor cannot ask us to write to
62 // arbitrary files. crash_reporter should only send us the base file name,
63 // so if it's not just a base file name, it's not from crash_reporter.
64 // Also check for "..", "/", and "."
65 if (file_name != file_name.BaseName() || file_name.ReferencesParent() ||
66 file_name.IsAbsolute() ||
67 file_name.value() == base::FilePath::kCurrentDirectory) {
68 DEBUGD_ADD_ERROR(error, kErrorBadFileName, "Bad File Name");
69 return false;
70 }
71
72 // Copy contents of file_descriptor to a new file named file_name inside
73 // crash_directory.
74 base::FilePath file_path = crash_directory.Append(file_name);
75 base::File new_file(file_path,
76 base::File::FLAG_CREATE | base::File::FLAG_WRITE);
77 if (!new_file.IsValid()) {
78 LOG(WARNING) << "Error creating file " << file_path.value() << ": "
79 << base::File::ErrorToString(new_file.error_details());
80 continue;
81 }
82
83 if (lseek(file_descriptor.get(), SEEK_SET, 0) == static_cast<off_t>(-1)) {
84 PLOG(WARNING) << "lseek failed";
85 }
86 const int kBufferSize = 1 << 16;
87 std::unique_ptr<char[]> buf(new char[kBufferSize]);
88 ssize_t len;
89 while ((len = HANDLE_EINTR(
90 read(file_descriptor.get(), buf.get(), kBufferSize))) > 0) {
91 if (!new_file.WriteAtCurrentPos(buf.get(), len)) {
92 LOG(WARNING) << "Error writing to file " << file_path.value() << ": "
93 << base::File::ErrorToString(new_file.error_details());
94 break;
95 }
96 }
97
98 if (len < 0) {
99 PLOG(WARNING) << "Failed to read from passed file descriptor";
100 }
101
102 // Ensure data is visible to crash_sender.
103 new_file.Flush();
104 }
105
106 // Since crash_sender jails itself, it won't actually see our /tmp/crash.###
107 // directory. Instead, open the directory and pass the /proc path to the
108 // directory file descriptor as the crash directory.
109 base::ScopedFD crash_directory_fd(
110 HANDLE_EINTR(open(crash_directory.value().c_str(), O_RDONLY)));
111 if (!crash_directory_fd.is_valid()) {
112 DEBUGD_ADD_ERROR(error, kErrorIOError, "Open directory failed");
113 return false;
114 }
115
116 base::FilePath munged_crash_directory("/proc/self/fd");
117 munged_crash_directory = munged_crash_directory.Append(
Qijiang Fan26fd9222020-03-31 20:16:40 +0900118 base::NumberToString(crash_directory_fd.get()));
Ian Barkley-Yeung7b7b0352019-04-23 12:52:04 -0700119
120 const bool ignore_hold_off_time = true; // We already flushed all the files.
121 RunCrashSender(ignore_hold_off_time, munged_crash_directory);
122
123 return true;
124}
125
126void CrashSenderTool::RunCrashSender(bool ignore_hold_off_time,
127 const base::FilePath& crash_directory) {
Jorge Lucangeli Obes623f8ca2014-09-18 10:50:06 -0700128 // 'crash_sender' requires accessing user mounts to upload user crashes.
129 ProcessWithId* p =
130 CreateProcess(false /* sandboxed */, true /* access_root_mount_ns */);
Mike Frysingerc0871522013-09-16 14:07:27 -0400131 p->AddArg("/sbin/crash_sender");
Ian Barkley-Yeung56ea2d42019-04-08 17:23:03 -0700132 // This is being invoked directly by the user. Override some of the limits
133 // we normally use to avoid interfering with user tasks.
Satoru Takabayashid3e8a2b2019-01-18 14:46:56 +0900134 p->AddArg("--max_spread_time=0");
Ian Barkley-Yeung56ea2d42019-04-08 17:23:03 -0700135 p->AddArg("--ignore_rate_limits");
Ian Barkley-Yeung7b7b0352019-04-23 12:52:04 -0700136
137 if (ignore_hold_off_time) {
138 p->AddArg("--ignore_hold_off_time");
139 }
140
141 if (!crash_directory.empty()) {
142 p->AddArg("--crash_directory=" + crash_directory.value());
143 }
144
Ian Barkley-Yeungc68cfc32019-07-15 16:43:57 -0700145 if (test_mode_) {
146 p->AddArg("--test_mode");
147 }
148
Mike Frysingerc0871522013-09-16 14:07:27 -0400149 p->Run();
150}
151
Ian Barkley-Yeungc68cfc32019-07-15 16:43:57 -0700152void CrashSenderTool::OnTestModeChanged(
Tom Hughesd6c2d392020-08-24 18:12:11 -0700153 const brillo::dbus_utils::ExportedPropertyBase* test_mode_property) {
Ian Barkley-Yeungc68cfc32019-07-15 16:43:57 -0700154 const auto* property =
155 dynamic_cast<const brillo::dbus_utils::ExportedProperty<bool>*>(
Tom Hughesd6c2d392020-08-24 18:12:11 -0700156 test_mode_property);
Ian Barkley-Yeungc68cfc32019-07-15 16:43:57 -0700157 DCHECK(property);
158 test_mode_ = property->value();
159 LOG(INFO) << "CrashSenderTestMode set to " << std::boolalpha << test_mode_;
160}
161
Ben Chana0011d82014-05-13 00:19:29 -0700162} // namespace debugd