1 // Copyright 2015 The Chromium Authors
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 "net/ssl/ssl_key_logger_impl.h"
6
7 #include <stdio.h>
8
9 #include <algorithm>
10 #include <utility>
11
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_file.h"
14 #include "base/functional/bind.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/sequence_checker.h"
19 #include "base/synchronization/lock.h"
20 #include "base/task/sequenced_task_runner.h"
21 #include "base/task/task_traits.h"
22 #include "base/task/thread_pool.h"
23 #include "base/thread_annotations.h"
24
25 namespace net {
26
27 namespace {
28 // Bound the number of outstanding writes to bound memory usage. Some
29 // antiviruses point this at a pipe and then read too slowly. See
30 // https://crbug.com/566951 and https://crbug.com/914880.
31 static constexpr size_t kMaxOutstandingLines = 512;
32 } // namespace
33
34 // An object which performs the blocking file operations on a background
35 // SequencedTaskRunner.
36 class SSLKeyLoggerImpl::Core
37 : public base::RefCountedThreadSafe<SSLKeyLoggerImpl::Core> {
38 public:
Core()39 Core() {
40 DETACH_FROM_SEQUENCE(sequence_checker_);
41 // That the user explicitly asked for debugging information would suggest
42 // waiting to flush these to disk, but some buggy antiviruses point this at
43 // a pipe and hang, so we avoid blocking shutdown. If writing to a real
44 // file, writes should complete quickly enough that this does not matter.
45 task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
46 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
47 }
48
49 Core(const Core&) = delete;
50 Core& operator=(const Core&) = delete;
51
SetFile(base::File file)52 void SetFile(base::File file) {
53 file_.reset(base::FileToFILE(std::move(file), "a"));
54 if (!file_)
55 DVLOG(1) << "Could not adopt file";
56 }
57
OpenFile(const base::FilePath & path)58 void OpenFile(const base::FilePath& path) {
59 task_runner_->PostTask(FROM_HERE,
60 base::BindOnce(&Core::OpenFileImpl, this, path));
61 }
62
WriteLine(const std::string & line)63 void WriteLine(const std::string& line) {
64 bool was_empty;
65 {
66 base::AutoLock lock(lock_);
67 was_empty = buffer_.empty();
68 if (buffer_.size() < kMaxOutstandingLines) {
69 buffer_.push_back(line);
70 } else {
71 lines_dropped_ = true;
72 }
73 }
74 if (was_empty) {
75 task_runner_->PostTask(FROM_HERE, base::BindOnce(&Core::Flush, this));
76 }
77 }
78
79 private:
80 friend class base::RefCountedThreadSafe<Core>;
81 ~Core() = default;
82
OpenFileImpl(const base::FilePath & path)83 void OpenFileImpl(const base::FilePath& path) {
84 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
85 DCHECK(!file_);
86 file_.reset(base::OpenFile(path, "a"));
87 if (!file_)
88 DVLOG(1) << "Could not open " << path.value();
89 }
90
Flush()91 void Flush() {
92 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
93
94 bool lines_dropped = false;
95 std::vector<std::string> buffer;
96 {
97 base::AutoLock lock(lock_);
98 std::swap(lines_dropped, lines_dropped_);
99 std::swap(buffer, buffer_);
100 }
101
102 if (file_) {
103 for (const auto& line : buffer) {
104 fprintf(file_.get(), "%s\n", line.c_str());
105 }
106 if (lines_dropped) {
107 fprintf(file_.get(), "# Some lines were dropped due to slow writes.\n");
108 }
109 fflush(file_.get());
110 }
111 }
112
113 scoped_refptr<base::SequencedTaskRunner> task_runner_;
114 base::ScopedFILE file_;
115 SEQUENCE_CHECKER(sequence_checker_);
116
117 base::Lock lock_;
118 bool lines_dropped_ GUARDED_BY(lock_) = false;
119 std::vector<std::string> buffer_ GUARDED_BY(lock_);
120 };
121
SSLKeyLoggerImpl(const base::FilePath & path)122 SSLKeyLoggerImpl::SSLKeyLoggerImpl(const base::FilePath& path)
123 : core_(base::MakeRefCounted<Core>()) {
124 core_->OpenFile(path);
125 }
126
SSLKeyLoggerImpl(base::File file)127 SSLKeyLoggerImpl::SSLKeyLoggerImpl(base::File file)
128 : core_(base::MakeRefCounted<Core>()) {
129 core_->SetFile(std::move(file));
130 }
131
132 SSLKeyLoggerImpl::~SSLKeyLoggerImpl() = default;
133
WriteLine(const std::string & line)134 void SSLKeyLoggerImpl::WriteLine(const std::string& line) {
135 core_->WriteLine(line);
136 }
137
138 } // namespace net
139