• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

google / santa / 12121220684

02 Dec 2024 02:17PM UTC coverage: 63.05% (-0.2%) from 63.269%
12121220684

Pull #1439

github

web-flow
Merge d8183d2bb into 737525b74
Pull Request #1439: Add null check in ProcessToken constuctor/destructor

5630 of 13458 branches covered (41.83%)

Branch coverage included in aggregate %.

0 of 9 new or added lines in 1 file covered. (0.0%)

7 existing lines in 1 file now uncovered.

18827 of 25332 relevant lines covered (74.32%)

6758.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

49.45
/Source/santad/ProcessTree/process_tree.cc
1
/// Copyright 2023 Google LLC
2
///
3
/// Licensed under the Apache License, Version 2.0 (the "License");
4
/// you may not use this file except in compliance with the License.
5
/// You may obtain a copy of the License at
6
///
7
///     https://www.apache.org/licenses/LICENSE-2.0
8
///
9
/// Unless required by applicable law or agreed to in writing, software
10
/// distributed under the License is distributed on an "AS IS" BASIS,
11
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
/// See the License for the specific language governing permissions and
13
/// limitations under the License.
14
#include "Source/santad/ProcessTree/process_tree.h"
15

16
#include <sys/types.h>
17

18
#include <algorithm>
19
#include <cassert>
20
#include <cstdint>
21
#include <functional>
22
#include <memory>
23
#include <optional>
24
#include <typeindex>
25
#include <utility>
26
#include <vector>
27

28
#include "Source/santad/ProcessTree/annotations/annotator.h"
29
#include "Source/santad/ProcessTree/process.h"
30
#include "Source/santad/ProcessTree/process_tree.pb.h"
31
#include "absl/container/flat_hash_map.h"
32
#include "absl/container/flat_hash_set.h"
33
#include "absl/status/status.h"
34
#include "absl/status/statusor.h"
35
#include "absl/synchronization/mutex.h"
36

37
namespace santa::santad::process_tree {
38

39
void ProcessTree::BackfillInsertChildren(
40
    absl::flat_hash_map<pid_t, std::vector<Process>> &parent_map,
41
    std::shared_ptr<Process> parent, const Process &unlinked_proc) {
×
42
  auto proc = std::make_shared<Process>(
×
43
      unlinked_proc.pid_, unlinked_proc.effective_cred_,
×
44
      // Re-use shared pointers from parent if value equivalent
45
      (parent && *(unlinked_proc.program_) == *(parent->program_))
×
46
          ? parent->program_
×
47
          : unlinked_proc.program_,
×
48
      parent);
×
49
  {
×
50
    absl::MutexLock lock(&mtx_);
×
51
    map_.emplace(unlinked_proc.pid_, proc);
×
52
  }
×
53

54
  // The only case where we should not have a parent is the root processes
55
  // (e.g. init, kthreadd).
56
  if (parent) {
×
57
    for (auto &annotator : annotators_) {
×
58
      annotator->AnnotateFork(*this, *(proc->parent_), *proc);
×
59
      if (proc->program_ != proc->parent_->program_) {
×
60
        annotator->AnnotateExec(*this, *(proc->parent_), *proc);
×
61
      }
×
62
    }
×
63
  }
×
64

65
  for (const Process &child : parent_map[unlinked_proc.pid_.pid]) {
×
66
    BackfillInsertChildren(parent_map, proc, child);
×
67
  }
×
68
}
×
69

70
void ProcessTree::HandleFork(uint64_t timestamp, const Process &parent,
71
                             const Pid new_pid) {
1,040✔
72
  if (Step(timestamp)) {
1,040!
73
    std::shared_ptr<Process> child;
1,040✔
74
    {
1,040✔
75
      absl::MutexLock lock(&mtx_);
1,040✔
76
      child = std::make_shared<Process>(new_pid, parent.effective_cred_,
1,040✔
77
                                        parent.program_, map_[parent.pid_]);
1,040✔
78
      map_.emplace(new_pid, child);
1,040✔
79
    }
1,040✔
80
    for (const auto &annotator : annotators_) {
1,040✔
81
      annotator->AnnotateFork(*this, parent, *child);
4✔
82
    }
4✔
83
  }
1,040✔
84
}
1,040✔
85

86
void ProcessTree::HandleExec(uint64_t timestamp, const Process &p,
87
                             const Pid new_pid, const Program prog,
88
                             const Cred c) {
5✔
89
  if (Step(timestamp)) {
5!
90
    // TODO(nickmg): should struct pid be reworked and only pid_version be
91
    // passed?
92
    assert(new_pid.pid == p.pid_.pid);
5✔
93

94
    auto new_proc = std::make_shared<Process>(
5✔
95
        new_pid, c, std::make_shared<const Program>(prog), p.parent_);
5✔
96
    {
5✔
97
      absl::MutexLock lock(&mtx_);
5✔
98
      remove_at_.push_back({timestamp, p.pid_});
5✔
99
      map_.emplace(new_proc->pid_, new_proc);
5✔
100
    }
5✔
101
    for (const auto &annotator : annotators_) {
5✔
102
      annotator->AnnotateExec(*this, p, *new_proc);
4✔
103
    }
4✔
104
  }
5✔
105
}
5✔
106

107
void ProcessTree::HandleExit(uint64_t timestamp, const Process &p) {
2✔
108
  if (Step(timestamp)) {
2!
109
    absl::MutexLock lock(&mtx_);
2✔
110
    remove_at_.push_back({timestamp, p.pid_});
2✔
111
  }
2✔
112
}
2✔
113

114
bool ProcessTree::Step(uint64_t timestamp) {
1,047✔
115
  absl::MutexLock lock(&mtx_);
1,047✔
116
  uint64_t new_cutoff = seen_timestamps_.front();
1,047✔
117
  if (timestamp < new_cutoff) {
1,047!
118
    // Event timestamp is before the rolling list of seen events.
119
    // This event may or may not have been processed, but be conservative and
120
    // do not reprocess.
121
    return false;
×
122
  }
×
123

124
  // seen_timestamps_ is sorted, so only look for the value if it's possibly
125
  // within the array.
126
  if (timestamp < seen_timestamps_.back()) {
1,047!
127
    // TODO(nickmg): If array is made bigger, replace with a binary search.
128
    for (const auto seen_ts : seen_timestamps_) {
×
129
      if (seen_ts == timestamp) {
×
130
        // Event seen, signal it should not be reprocessed.
131
        return false;
×
132
      }
×
133
    }
×
134
  }
×
135

136
  auto insert_point =
1,047✔
137
      std::find_if(seen_timestamps_.rbegin(), seen_timestamps_.rend(),
1,047✔
138
                   [&](uint64_t x) { return x < timestamp; });
1,047✔
139
  std::move(seen_timestamps_.begin() + 1, insert_point.base(),
1,047✔
140
            seen_timestamps_.begin());
1,047✔
141
  *insert_point = timestamp;
1,047✔
142

143
  for (auto it = remove_at_.begin(); it != remove_at_.end();) {
1,117✔
144
    if (it->first < new_cutoff) {
70✔
145
      if (auto target = GetLocked(it->second);
2✔
146
          target && (*target)->refcnt_ > 0) {
2!
147
        (*target)->tombstoned_ = true;
1✔
148
      } else {
1✔
149
        map_.erase(it->second);
1✔
150
      }
1✔
151
      it = remove_at_.erase(it);
2✔
152
    } else {
68✔
153
      it++;
68✔
154
    }
68✔
155
  }
70✔
156

157
  return true;
1,047✔
158
}
1,047✔
159

160
void ProcessTree::RetainProcess(std::vector<struct Pid> &pids) {
1✔
161
  absl::MutexLock lock(&mtx_);
1✔
162
  for (const struct Pid &p : pids) {
1✔
163
    auto proc = GetLocked(p);
1✔
164
    if (proc) {
1!
165
      (*proc)->refcnt_++;
1✔
166
    }
1✔
167
  }
1✔
168
}
1✔
169

170
void ProcessTree::ReleaseProcess(std::vector<struct Pid> &pids) {
1✔
171
  absl::MutexLock lock(&mtx_);
1✔
172
  for (const struct Pid &p : pids) {
1✔
173
    auto proc = GetLocked(p);
1✔
174
    if (proc) {
1!
175
      if (--(*proc)->refcnt_ == 0 && (*proc)->tombstoned_) {
1!
176
        map_.erase(p);
1✔
177
      }
1✔
178
    }
1✔
179
  }
1✔
180
}
1✔
181

182
/*
183
---
184
Annotation get/set
185
---
186
*/
187

188
void ProcessTree::AnnotateProcess(const Process &p,
189
                                  std::shared_ptr<const Annotator> a) {
6✔
190
  absl::MutexLock lock(&mtx_);
6✔
191
  const Annotator &x = *a;
6✔
192
  map_[p.pid_]->annotations_.emplace(std::type_index(typeid(x)), std::move(a));
6✔
193
}
6✔
194

195
std::optional<::santa::pb::v1::process_tree::Annotations>
196
ProcessTree::ExportAnnotations(const Pid p) {
×
197
  auto proc = Get(p);
×
198
  if (!proc || (*proc)->annotations_.empty()) {
×
199
    return std::nullopt;
×
200
  }
×
201
  ::santa::pb::v1::process_tree::Annotations a;
×
202
  for (const auto &[_, annotation] : (*proc)->annotations_) {
×
203
    if (auto x = annotation->Proto(); x) a.MergeFrom(*x);
×
204
  }
×
205
  return a;
×
206
}
×
207

208
/*
209
---
210
Tree inspection methods
211
---
212
*/
213

214
std::vector<std::shared_ptr<const Process>> ProcessTree::RootSlice(
215
    std::shared_ptr<const Process> p) const {
×
216
  std::vector<std::shared_ptr<const Process>> slice;
×
217
  while (p) {
×
218
    slice.push_back(p);
×
219
    p = p->parent_;
×
220
  }
×
221
  return slice;
×
222
}
×
223

224
void ProcessTree::Iterate(
225
    std::function<void(std::shared_ptr<const Process> p)> f) const {
×
226
  std::vector<std::shared_ptr<const Process>> procs;
×
227
  {
×
228
    absl::ReaderMutexLock lock(&mtx_);
×
229
    procs.reserve(map_.size());
×
230
    for (auto &[_, proc] : map_) {
×
231
      procs.push_back(proc);
×
232
    }
×
233
  }
×
234

235
  for (auto &p : procs) {
×
236
    f(p);
×
237
  }
×
238
}
×
239

240
std::optional<std::shared_ptr<const Process>> ProcessTree::Get(
241
    const Pid target) const {
1,017✔
242
  absl::ReaderMutexLock lock(&mtx_);
1,017✔
243
  return GetLocked(target);
1,017✔
244
}
1,017✔
245

246
std::optional<std::shared_ptr<Process>> ProcessTree::GetLocked(
247
    const Pid target) const {
1,021✔
248
  auto it = map_.find(target);
1,021✔
249
  if (it == map_.end()) {
1,021✔
250
    return std::nullopt;
2✔
251
  }
2✔
252
  return it->second;
1,019✔
253
}
1,021✔
254

255
std::shared_ptr<const Process> ProcessTree::GetParent(const Process &p) const {
1✔
256
  return p.parent_;
1✔
257
}
1✔
258

259
#if SANTA_PROCESS_TREE_DEBUG
260
void ProcessTree::DebugDump(std::ostream &stream) const {
261
  absl::ReaderMutexLock lock(&mtx_);
262
  stream << map_.size() << " processes" << std::endl;
263
  DebugDumpLocked(stream, 0, 0);
264
}
265

266
void ProcessTree::DebugDumpLocked(std::ostream &stream, int depth,
267
                                  pid_t ppid) const
268
    ABSL_SHARED_LOCKS_REQUIRED(mtx_) {
269
  for (auto &[_, process] : map_) {
270
    if ((ppid == 0 && !process->parent_) ||
271
        (process->parent_ && process->parent_->pid_.pid == ppid)) {
272
      stream << std::string(2 * depth, ' ') << process->pid_.pid
273
             << process->program_->executable << std::endl;
274
      DebugDumpLocked(stream, depth + 1, process->pid_.pid);
275
    }
276
  }
277
}
278
#endif
279

280
absl::StatusOr<std::shared_ptr<ProcessTree>> CreateTree(
281
    std::vector<std::unique_ptr<Annotator>> annotations) {
24✔
282
  absl::flat_hash_set<std::type_index> seen;
24✔
283
  for (const auto &annotator : annotations) {
24!
284
    if (seen.count(std::type_index(typeid(annotator)))) {
×
285
      return absl::InvalidArgumentError(
×
286
          "Multiple annotators of the same class");
×
287
    }
×
288
    seen.emplace(std::type_index(typeid(annotator)));
×
289
  }
×
290

291
  if (seen.empty()) {
24!
292
    return nullptr;
24✔
293
  }
24✔
294

295
  auto tree = std::make_shared<ProcessTree>(std::move(annotations));
×
296
  if (auto status = tree->Backfill(); !status.ok()) {
×
297
    return status;
×
298
  }
×
299
  return tree;
×
300
}
×
301

302
/*
303
----
304
Tokens
305
----
306
*/
307

308
ProcessToken::ProcessToken(std::shared_ptr<ProcessTree> tree,
309
                           std::vector<struct Pid> pids)
310
    : tree_(std::move(tree)), pids_(std::move(pids)) {
×
NEW
311
  if (tree_ == nullptr) {
×
NEW
312
    return;
×
NEW
313
  }
×
314
  tree_->RetainProcess(pids);
×
315
}
×
316

NEW
317
ProcessToken::~ProcessToken() {
×
NEW
318
  if (tree_ == nullptr) {
×
NEW
319
    return;
×
NEW
320
  }
×
NEW
321
  tree_->ReleaseProcess(pids_);
×
NEW
322
}
×
323

324
}  // namespace santa::santad::process_tree
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc