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

google / santa / 12242432202

09 Dec 2024 07:00PM UTC coverage: 62.914% (-0.4%) from 63.269%
12242432202

Pull #1440

github

web-flow
Merge d399f0a54 into 737525b74
Pull Request #1440: Add ancestry annotator

5631 of 13490 branches covered (41.74%)

Branch coverage included in aggregate %.

1 of 62 new or added lines in 5 files covered. (1.61%)

7 existing lines in 1 file now uncovered.

18827 of 25385 relevant lines covered (74.17%)

5816.96 hits per line

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

46.53
/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
std::vector<::santa::pb::v1::process_tree::ProcessID> ProcessTree::GetAncestors(
NEW
71
    const Process &process) {
×
NEW
72
  std::vector<::santa::pb::v1::process_tree::ProcessID> ancestors;
×
NEW
73
  if (!process.parent_) {
×
NEW
74
    return ancestors;
×
NEW
75
  }
×
NEW
76
  {
×
NEW
77
    absl::MutexLock lock(&mtx_);
×
NEW
78
    std::optional<std::shared_ptr<Process>> chainProcess =
×
NEW
79
        GetLocked(process.parent_->pid_);
×
NEW
80
    while (chainProcess != std::nullopt) {
×
NEW
81
      ::santa::pb::v1::process_tree::ProcessID process_id;
×
NEW
82
      process_id.set_pid((*chainProcess)->pid_.pid);
×
NEW
83
      process_id.set_pidversion((*chainProcess)->pid_.pidversion);
×
NEW
84
      ancestors.push_back(process_id);
×
NEW
85
      if ((*chainProcess)->parent_) {
×
NEW
86
        chainProcess = GetLocked((*chainProcess)->parent_->pid_);
×
NEW
87
      } else {
×
NEW
88
        chainProcess = std::nullopt;
×
NEW
89
      }
×
NEW
90
    }
×
NEW
91
  }
×
NEW
92
  return ancestors;
×
NEW
93
}
×
94

95
void ProcessTree::HandleFork(uint64_t timestamp, const Process &parent,
96
                             const Pid new_pid) {
1,040✔
97
  if (Step(timestamp)) {
1,040!
98
    std::shared_ptr<Process> child;
1,040✔
99
    {
1,040✔
100
      absl::MutexLock lock(&mtx_);
1,040✔
101
      child = std::make_shared<Process>(new_pid, parent.effective_cred_,
1,040✔
102
                                        parent.program_, map_[parent.pid_]);
1,040✔
103
      map_.emplace(new_pid, child);
1,040✔
104
    }
1,040✔
105
    for (const auto &annotator : annotators_) {
1,040✔
106
      annotator->AnnotateFork(*this, parent, *child);
4✔
107
    }
4✔
108
  }
1,040✔
109
}
1,040✔
110

111
void ProcessTree::HandleExec(uint64_t timestamp, const Process &p,
112
                             const Pid new_pid, const Program prog,
113
                             const Cred c) {
5✔
114
  if (Step(timestamp)) {
5!
115
    // TODO(nickmg): should struct pid be reworked and only pid_version be
116
    // passed?
117
    assert(new_pid.pid == p.pid_.pid);
5✔
118

119
    auto new_proc = std::make_shared<Process>(
5✔
120
        new_pid, c, std::make_shared<const Program>(prog), p.parent_);
5✔
121
    {
5✔
122
      absl::MutexLock lock(&mtx_);
5✔
123
      remove_at_.push_back({timestamp, p.pid_});
5✔
124
      map_.emplace(new_proc->pid_, new_proc);
5✔
125
    }
5✔
126
    for (const auto &annotator : annotators_) {
5✔
127
      annotator->AnnotateExec(*this, p, *new_proc);
4✔
128
    }
4✔
129
  }
5✔
130
}
5✔
131

132
void ProcessTree::HandleExit(uint64_t timestamp, const Process &p) {
2✔
133
  if (Step(timestamp)) {
2!
134
    absl::MutexLock lock(&mtx_);
2✔
135
    remove_at_.push_back({timestamp, p.pid_});
2✔
136
  }
2✔
137
}
2✔
138

139
bool ProcessTree::Step(uint64_t timestamp) {
1,047✔
140
  absl::MutexLock lock(&mtx_);
1,047✔
141
  uint64_t new_cutoff = seen_timestamps_.front();
1,047✔
142
  if (timestamp < new_cutoff) {
1,047!
143
    // Event timestamp is before the rolling list of seen events.
144
    // This event may or may not have been processed, but be conservative and
145
    // do not reprocess.
146
    return false;
×
147
  }
×
148

149
  // seen_timestamps_ is sorted, so only look for the value if it's possibly
150
  // within the array.
151
  if (timestamp < seen_timestamps_.back()) {
1,047!
152
    // TODO(nickmg): If array is made bigger, replace with a binary search.
153
    for (const auto seen_ts : seen_timestamps_) {
×
154
      if (seen_ts == timestamp) {
×
155
        // Event seen, signal it should not be reprocessed.
156
        return false;
×
157
      }
×
158
    }
×
159
  }
×
160

161
  auto insert_point =
1,047✔
162
      std::find_if(seen_timestamps_.rbegin(), seen_timestamps_.rend(),
1,047✔
163
                   [&](uint64_t x) { return x < timestamp; });
1,047✔
164
  std::move(seen_timestamps_.begin() + 1, insert_point.base(),
1,047✔
165
            seen_timestamps_.begin());
1,047✔
166
  *insert_point = timestamp;
1,047✔
167

168
  for (auto it = remove_at_.begin(); it != remove_at_.end();) {
1,117✔
169
    if (it->first < new_cutoff) {
70✔
170
      if (auto target = GetLocked(it->second);
2✔
171
          target && (*target)->refcnt_ > 0) {
2!
172
        (*target)->tombstoned_ = true;
1✔
173
      } else {
1✔
174
        map_.erase(it->second);
1✔
175
      }
1✔
176
      it = remove_at_.erase(it);
2✔
177
    } else {
68✔
178
      it++;
68✔
179
    }
68✔
180
  }
70✔
181

182
  return true;
1,047✔
183
}
1,047✔
184

185
void ProcessTree::RetainProcess(std::vector<struct Pid> &pids) {
1✔
186
  absl::MutexLock lock(&mtx_);
1✔
187
  for (const struct Pid &p : pids) {
1✔
188
    auto proc = GetLocked(p);
1✔
189
    if (proc) {
1!
190
      (*proc)->refcnt_++;
1✔
191
    }
1✔
192
  }
1✔
193
}
1✔
194

195
void ProcessTree::ReleaseProcess(std::vector<struct Pid> &pids) {
1✔
196
  absl::MutexLock lock(&mtx_);
1✔
197
  for (const struct Pid &p : pids) {
1✔
198
    auto proc = GetLocked(p);
1✔
199
    if (proc) {
1!
200
      if (--(*proc)->refcnt_ == 0 && (*proc)->tombstoned_) {
1!
201
        map_.erase(p);
1✔
202
      }
1✔
203
    }
1✔
204
  }
1✔
205
}
1✔
206

207
/*
208
---
209
Annotation get/set
210
---
211
*/
212

213
void ProcessTree::AnnotateProcess(const Process &p,
214
                                  std::shared_ptr<const Annotator> a) {
6✔
215
  absl::MutexLock lock(&mtx_);
6✔
216
  const Annotator &x = *a;
6✔
217
  map_[p.pid_]->annotations_.emplace(std::type_index(typeid(x)), std::move(a));
6✔
218
}
6✔
219

220
std::optional<::santa::pb::v1::process_tree::Annotations>
221
ProcessTree::ExportAnnotations(const Pid p) {
×
222
  auto proc = Get(p);
×
223
  if (!proc || (*proc)->annotations_.empty()) {
×
224
    return std::nullopt;
×
225
  }
×
226
  ::santa::pb::v1::process_tree::Annotations a;
×
227
  for (const auto &[_, annotation] : (*proc)->annotations_) {
×
228
    if (auto x = annotation->Proto(); x) a.MergeFrom(*x);
×
229
  }
×
230
  return a;
×
231
}
×
232

233
/*
234
---
235
Tree inspection methods
236
---
237
*/
238

239
std::vector<std::shared_ptr<const Process>> ProcessTree::RootSlice(
240
    std::shared_ptr<const Process> p) const {
×
241
  std::vector<std::shared_ptr<const Process>> slice;
×
242
  while (p) {
×
243
    slice.push_back(p);
×
244
    p = p->parent_;
×
245
  }
×
246
  return slice;
×
247
}
×
248

249
void ProcessTree::Iterate(
250
    std::function<void(std::shared_ptr<const Process> p)> f) const {
×
251
  std::vector<std::shared_ptr<const Process>> procs;
×
252
  {
×
253
    absl::ReaderMutexLock lock(&mtx_);
×
254
    procs.reserve(map_.size());
×
255
    for (auto &[_, proc] : map_) {
×
256
      procs.push_back(proc);
×
257
    }
×
258
  }
×
259

260
  for (auto &p : procs) {
×
261
    f(p);
×
262
  }
×
263
}
×
264

265
std::optional<std::shared_ptr<const Process>> ProcessTree::Get(
266
    const Pid target) const {
1,017✔
267
  absl::ReaderMutexLock lock(&mtx_);
1,017✔
268
  return GetLocked(target);
1,017✔
269
}
1,017✔
270

271
std::optional<std::shared_ptr<Process>> ProcessTree::GetLocked(
272
    const Pid target) const {
1,021✔
273
  auto it = map_.find(target);
1,021✔
274
  if (it == map_.end()) {
1,021✔
275
    return std::nullopt;
2✔
276
  }
2✔
277
  return it->second;
1,019✔
278
}
1,021✔
279

280
std::shared_ptr<const Process> ProcessTree::GetParent(const Process &p) const {
1✔
281
  return p.parent_;
1✔
282
}
1✔
283

284
#if SANTA_PROCESS_TREE_DEBUG
285
void ProcessTree::DebugDump(std::ostream &stream) const {
286
  absl::ReaderMutexLock lock(&mtx_);
287
  stream << map_.size() << " processes" << std::endl;
288
  DebugDumpLocked(stream, 0, 0);
289
}
290

291
void ProcessTree::DebugDumpLocked(std::ostream &stream, int depth,
292
                                  pid_t ppid) const
293
    ABSL_SHARED_LOCKS_REQUIRED(mtx_) {
294
  for (auto &[_, process] : map_) {
295
    if ((ppid == 0 && !process->parent_) ||
296
        (process->parent_ && process->parent_->pid_.pid == ppid)) {
297
      stream << std::string(2 * depth, ' ') << process->pid_.pid
298
             << process->program_->executable << std::endl;
299
      DebugDumpLocked(stream, depth + 1, process->pid_.pid);
300
    }
301
  }
302
}
303
#endif
304

305
absl::StatusOr<std::shared_ptr<ProcessTree>> CreateTree(
306
    std::vector<std::unique_ptr<Annotator>> annotations) {
24✔
307
  absl::flat_hash_set<std::type_index> seen;
24✔
308
  for (const auto &annotator : annotations) {
24!
309
    if (seen.count(std::type_index(typeid(annotator)))) {
×
310
      return absl::InvalidArgumentError(
×
311
          "Multiple annotators of the same class");
×
312
    }
×
313
    seen.emplace(std::type_index(typeid(annotator)));
×
314
  }
×
315

316
  if (seen.empty()) {
24!
317
    return nullptr;
24✔
318
  }
24✔
319

320
  auto tree = std::make_shared<ProcessTree>(std::move(annotations));
×
321
  if (auto status = tree->Backfill(); !status.ok()) {
×
322
    return status;
×
323
  }
×
324
  return tree;
×
325
}
×
326

327
/*
328
----
329
Tokens
330
----
331
*/
332

333
ProcessToken::ProcessToken(std::shared_ptr<ProcessTree> tree,
334
                           std::vector<struct Pid> pids)
335
    : tree_(std::move(tree)), pids_(std::move(pids)) {
×
336
  tree_->RetainProcess(pids);
×
337
}
×
338

339
ProcessToken::~ProcessToken() { tree_->ReleaseProcess(pids_); }
×
340

341
}  // 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