mirror of
https://github.com/google/santa.git
synced 2026-04-24 03:00:12 -04:00
ProcessTree: add core process tree logic (1/4) (#1236)
* ProcessTree: add core process tree logic * make Step implicitly called by Handle* methods * lint * naming convention * widen pidversion to be generic * move os specific backfill to os specific impl * simplify ts checking * retain/release a whole vec of pids * document processtoken * lint * namespace * add process tree to project-wide unit test target * case change annotations * case change annotations * remove stray comment * default initialize seen_timestamps * fix missing initialization of refcnt and tombstoned * reshuffle pb namespace * pr review * move annotation registration to tree construction * use factory function for tree construction
This commit is contained in:
@@ -1387,6 +1387,7 @@ test_suite(
|
||||
":SantadTest",
|
||||
":WatchItemsTest",
|
||||
"//Source/santad/Logs/EndpointSecurity/Writers/FSSpool:fsspool_test",
|
||||
"//Source/santad/ProcessTree:process_tree_test",
|
||||
],
|
||||
visibility = ["//:santa_package_group"],
|
||||
)
|
||||
|
||||
67
Source/santad/ProcessTree/BUILD
Normal file
67
Source/santad/ProcessTree/BUILD
Normal file
@@ -0,0 +1,67 @@
|
||||
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
|
||||
load("//:helper.bzl", "santa_unit_test")
|
||||
|
||||
package(
|
||||
default_visibility = ["//:santa_package_group"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "process",
|
||||
hdrs = ["process.h"],
|
||||
deps = [
|
||||
"//Source/santad/ProcessTree/annotations:annotator",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "process_tree",
|
||||
srcs = [
|
||||
"process_tree.cc",
|
||||
"process_tree_macos.mm",
|
||||
],
|
||||
hdrs = ["process_tree.h"],
|
||||
sdk_dylibs = [
|
||||
"bsm",
|
||||
],
|
||||
deps = [
|
||||
":process",
|
||||
"//Source/santad/ProcessTree/annotations:annotator",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/container:flat_hash_set",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
proto_library(
|
||||
name = "process_tree_proto",
|
||||
srcs = ["process_tree.proto"],
|
||||
)
|
||||
|
||||
cc_proto_library(
|
||||
name = "process_tree_cc_proto",
|
||||
deps = [":process_tree_proto"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "process_tree_test_helpers",
|
||||
srcs = ["process_tree_test_helpers.mm"],
|
||||
hdrs = ["process_tree_test_helpers.h"],
|
||||
deps = [
|
||||
":process_tree",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "process_tree_test",
|
||||
srcs = ["process_tree_test.mm"],
|
||||
deps = [
|
||||
":process",
|
||||
":process_tree_test_helpers",
|
||||
"//Source/santad/ProcessTree/annotations:annotator",
|
||||
],
|
||||
)
|
||||
11
Source/santad/ProcessTree/annotations/BUILD
Normal file
11
Source/santad/ProcessTree/annotations/BUILD
Normal file
@@ -0,0 +1,11 @@
|
||||
package(
|
||||
default_visibility = ["//:santa_package_group"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "annotator",
|
||||
hdrs = ["Annotator.h"],
|
||||
deps = [
|
||||
"//Source/santad/ProcessTree:process_tree_cc_proto",
|
||||
],
|
||||
)
|
||||
39
Source/santad/ProcessTree/annotations/annotator.h
Normal file
39
Source/santad/ProcessTree/annotations/annotator.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/// Copyright 2023 Google LLC
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
#ifndef SANTA__SANTAD_PROCESSTREE_ANNOTATIONS_BASE_H
|
||||
#define SANTA__SANTAD_PROCESSTREE_ANNOTATIONS_BASE_H
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "Source/santad/ProcessTree/process_tree.pb.h"
|
||||
|
||||
namespace santa::santad::process_tree {
|
||||
|
||||
class ProcessTree;
|
||||
class Process;
|
||||
|
||||
class Annotator {
|
||||
public:
|
||||
virtual ~Annotator() = default;
|
||||
|
||||
virtual void AnnotateFork(ProcessTree &tree, const Process &parent,
|
||||
const Process &child) = 0;
|
||||
virtual void AnnotateExec(ProcessTree &tree, const Process &orig_process,
|
||||
const Process &new_process) = 0;
|
||||
virtual std::optional<::santa::pb::v1::process_tree::Annotations> Proto() const = 0;
|
||||
};
|
||||
|
||||
} // namespace santa::santad::process_tree
|
||||
|
||||
#endif
|
||||
113
Source/santad/ProcessTree/process.h
Normal file
113
Source/santad/ProcessTree/process.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/// Copyright 2023 Google LLC
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
#ifndef SANTA__SANTAD_PROCESSTREE_PROCESS_H
|
||||
#define SANTA__SANTAD_PROCESSTREE_PROCESS_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <vector>
|
||||
|
||||
#include "Source/santad/ProcessTree/annotations/annotator.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
|
||||
namespace santa::santad::process_tree {
|
||||
|
||||
struct Pid {
|
||||
pid_t pid;
|
||||
uint64_t pidversion;
|
||||
|
||||
friend bool operator==(const struct Pid &lhs, const struct Pid &rhs) {
|
||||
return lhs.pid == rhs.pid && lhs.pidversion == rhs.pidversion;
|
||||
}
|
||||
friend bool operator!=(const struct Pid &lhs, const struct Pid &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename H>
|
||||
H AbslHashValue(H h, const struct Pid &p) {
|
||||
return H::combine(std::move(h), p.pid, p.pidversion);
|
||||
}
|
||||
|
||||
struct Cred {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
friend bool operator==(const struct Cred &lhs, const struct Cred &rhs) {
|
||||
return lhs.uid == rhs.uid && lhs.gid == rhs.gid;
|
||||
}
|
||||
friend bool operator!=(const struct Cred &lhs, const struct Cred &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct Program {
|
||||
std::string executable;
|
||||
std::vector<std::string> arguments;
|
||||
|
||||
friend bool operator==(const struct Program &lhs, const struct Program &rhs) {
|
||||
return lhs.executable == rhs.executable && lhs.arguments == rhs.arguments;
|
||||
}
|
||||
friend bool operator!=(const struct Program &lhs, const struct Program &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
// Fwd decls
|
||||
class ProcessTree;
|
||||
|
||||
class Process {
|
||||
public:
|
||||
explicit Process(const Pid pid, const Cred cred,
|
||||
std::shared_ptr<const Program> program,
|
||||
std::shared_ptr<const Process> parent)
|
||||
: pid_(pid),
|
||||
effective_cred_(cred),
|
||||
program_(program),
|
||||
annotations_(),
|
||||
parent_(parent),
|
||||
refcnt_(0),
|
||||
tombstoned_(false) {}
|
||||
Process(const Process &) = default;
|
||||
Process& operator=(const Process &) = delete;
|
||||
Process(Process &&) = default;
|
||||
Process& operator=(Process &&) = delete;
|
||||
|
||||
// Const "attributes" are public
|
||||
const struct Pid pid_;
|
||||
const struct Cred effective_cred_;
|
||||
const std::shared_ptr<const Program> program_;
|
||||
|
||||
private:
|
||||
// This is not API.
|
||||
// The tree helper methods are the API, and we just happen to implement
|
||||
// annotation storage and the parent relation in memory on the process right
|
||||
// now.
|
||||
friend class ProcessTree;
|
||||
absl::flat_hash_map<std::type_index, std::shared_ptr<const Annotator>>
|
||||
annotations_;
|
||||
std::shared_ptr<const Process> parent_;
|
||||
// TODO(nickmg): atomic here breaks the build.
|
||||
int refcnt_;
|
||||
// If the process is tombstoned, the event removing it from the tree has been
|
||||
// processed, but refcnt>0 keeps it alive.
|
||||
bool tombstoned_;
|
||||
};
|
||||
|
||||
} // namespace santa::santad::process_tree
|
||||
|
||||
#endif
|
||||
300
Source/santad/ProcessTree/process_tree.cc
Normal file
300
Source/santad/ProcessTree/process_tree.cc
Normal file
@@ -0,0 +1,300 @@
|
||||
/// Copyright 2023 Google LLC
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
#include "Source/santad/ProcessTree/process_tree.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
|
||||
#include "Source/santad/ProcessTree/annotations/annotator.h"
|
||||
#include "Source/santad/ProcessTree/process.h"
|
||||
#include "Source/santad/ProcessTree/process_tree.pb.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
|
||||
namespace santa::santad::process_tree {
|
||||
|
||||
void ProcessTree::BackfillInsertChildren(
|
||||
absl::flat_hash_map<pid_t, std::vector<const Process>> &parent_map,
|
||||
std::shared_ptr<Process> parent, const Process &unlinked_proc) {
|
||||
auto proc = std::make_shared<Process>(
|
||||
unlinked_proc.pid_, unlinked_proc.effective_cred_,
|
||||
// Re-use shared pointers from parent if value equivalent
|
||||
(parent && *(unlinked_proc.program_) == *(parent->program_))
|
||||
? parent->program_
|
||||
: unlinked_proc.program_,
|
||||
parent);
|
||||
{
|
||||
absl::MutexLock lock(&mtx_);
|
||||
map_.emplace(unlinked_proc.pid_, proc);
|
||||
}
|
||||
|
||||
// The only case where we should not have a parent is the root processes
|
||||
// (e.g. init, kthreadd).
|
||||
if (parent) {
|
||||
for (auto &annotator : annotators_) {
|
||||
annotator->AnnotateFork(*this, *(proc->parent_), *proc);
|
||||
if (proc->program_ != proc->parent_->program_) {
|
||||
annotator->AnnotateExec(*this, *(proc->parent_), *proc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const Process &child : parent_map[unlinked_proc.pid_.pid]) {
|
||||
BackfillInsertChildren(parent_map, proc, child);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessTree::HandleFork(uint64_t timestamp, const Process &parent,
|
||||
const Pid new_pid) {
|
||||
if (Step(timestamp)) {
|
||||
std::shared_ptr<Process> child;
|
||||
{
|
||||
absl::MutexLock lock(&mtx_);
|
||||
child = std::make_shared<Process>(new_pid, parent.effective_cred_,
|
||||
parent.program_, map_[parent.pid_]);
|
||||
map_.emplace(new_pid, child);
|
||||
}
|
||||
for (const auto &annotator : annotators_) {
|
||||
annotator->AnnotateFork(*this, parent, *child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessTree::HandleExec(uint64_t timestamp, const Process &p,
|
||||
const Pid new_pid, const Program prog,
|
||||
const Cred c) {
|
||||
if (Step(timestamp)) {
|
||||
// TODO(nickmg): should struct pid be reworked and only pid_version be
|
||||
// passed?
|
||||
assert(new_pid.pid == p.pid_.pid);
|
||||
|
||||
auto new_proc = std::make_shared<Process>(
|
||||
new_pid, c, std::make_shared<const Program>(prog), p.parent_);
|
||||
{
|
||||
absl::MutexLock lock(&mtx_);
|
||||
remove_at_.push_back({timestamp, p.pid_});
|
||||
map_.emplace(new_proc->pid_, new_proc);
|
||||
}
|
||||
for (const auto &annotator : annotators_) {
|
||||
annotator->AnnotateExec(*this, p, *new_proc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessTree::HandleExit(uint64_t timestamp, const Process &p) {
|
||||
if (Step(timestamp)) {
|
||||
absl::MutexLock lock(&mtx_);
|
||||
remove_at_.push_back({timestamp, p.pid_});
|
||||
}
|
||||
}
|
||||
|
||||
bool ProcessTree::Step(uint64_t timestamp) {
|
||||
absl::MutexLock lock(&mtx_);
|
||||
uint64_t new_cutoff = seen_timestamps_.front();
|
||||
if (timestamp < new_cutoff) {
|
||||
// Event timestamp is before the rolling list of seen events.
|
||||
// This event may or may not have been processed, but be conservative and
|
||||
// do not reprocess.
|
||||
return false;
|
||||
}
|
||||
|
||||
// seen_timestamps_ is sorted, so only look for the value if it's possibly within the array.
|
||||
if (timestamp < seen_timestamps_.back()) {
|
||||
// TODO(nickmg): If array is made bigger, replace with a binary search.
|
||||
for (const auto seen_ts : seen_timestamps_) {
|
||||
if (seen_ts == timestamp) {
|
||||
// Event seen, signal it should not be reprocessed.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto insert_point =
|
||||
std::find_if(seen_timestamps_.rbegin(), seen_timestamps_.rend(),
|
||||
[&](uint64_t x) { return x < timestamp; });
|
||||
std::move(seen_timestamps_.begin() + 1, insert_point.base(),
|
||||
seen_timestamps_.begin());
|
||||
*insert_point = timestamp;
|
||||
|
||||
for (auto it = remove_at_.begin(); it != remove_at_.end();) {
|
||||
if (it->first < new_cutoff) {
|
||||
if (auto target = GetLocked(it->second);
|
||||
target && (*target)->refcnt_ > 0) {
|
||||
(*target)->tombstoned_ = true;
|
||||
} else {
|
||||
map_.erase(it->second);
|
||||
}
|
||||
it = remove_at_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProcessTree::RetainProcess(std::vector<struct Pid> &pids) {
|
||||
absl::MutexLock lock(&mtx_);
|
||||
for (const struct Pid &p : pids) {
|
||||
auto proc = GetLocked(p);
|
||||
if (proc) {
|
||||
(*proc)->refcnt_++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessTree::ReleaseProcess(std::vector<struct Pid> &pids) {
|
||||
absl::MutexLock lock(&mtx_);
|
||||
for (const struct Pid &p : pids) {
|
||||
auto proc = GetLocked(p);
|
||||
if (proc) {
|
||||
if (--(*proc)->refcnt_ == 0 && (*proc)->tombstoned_) {
|
||||
map_.erase(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
---
|
||||
Annotation get/set
|
||||
---
|
||||
*/
|
||||
|
||||
void ProcessTree::AnnotateProcess(const Process &p,
|
||||
std::shared_ptr<const Annotator> a) {
|
||||
absl::MutexLock lock(&mtx_);
|
||||
const Annotator &x = *a;
|
||||
map_[p.pid_]->annotations_.emplace(std::type_index(typeid(x)), std::move(a));
|
||||
}
|
||||
|
||||
std::optional<::santa::pb::v1::process_tree::Annotations> ProcessTree::ExportAnnotations(const Pid p) {
|
||||
auto proc = Get(p);
|
||||
if (!proc || (*proc)->annotations_.size() == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
::santa::pb::v1::process_tree::Annotations a;
|
||||
for (const auto &[_, annotation] : (*proc)->annotations_) {
|
||||
if (auto x = annotation->Proto(); x) a.MergeFrom(*x);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/*
|
||||
---
|
||||
Tree inspection methods
|
||||
---
|
||||
*/
|
||||
|
||||
std::vector<std::shared_ptr<const Process>> ProcessTree::RootSlice(
|
||||
std::shared_ptr<const Process> p) const {
|
||||
std::vector<std::shared_ptr<const Process>> slice;
|
||||
while (p) {
|
||||
slice.push_back(p);
|
||||
p = p->parent_;
|
||||
}
|
||||
return slice;
|
||||
}
|
||||
|
||||
void ProcessTree::Iterate(
|
||||
std::function<void(std::shared_ptr<const Process> p)> f) const {
|
||||
std::vector<std::shared_ptr<const Process>> procs;
|
||||
{
|
||||
absl::ReaderMutexLock lock(&mtx_);
|
||||
procs.reserve(map_.size());
|
||||
for (auto &[_, proc] : map_) {
|
||||
procs.push_back(proc);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &p : procs) {
|
||||
f(p);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<const Process>> ProcessTree::Get(
|
||||
const Pid target) const {
|
||||
absl::ReaderMutexLock lock(&mtx_);
|
||||
return GetLocked(target);
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Process>> ProcessTree::GetLocked(
|
||||
const Pid target) const {
|
||||
auto it = map_.find(target);
|
||||
if (it == map_.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<const Process> ProcessTree::GetParent(const Process &p) const {
|
||||
return p.parent_;
|
||||
}
|
||||
|
||||
#if SANTA_PROCESS_TREE_DEBUG
|
||||
void ProcessTree::DebugDump(std::ostream &stream) const {
|
||||
absl::ReaderMutexLock lock(&mtx_);
|
||||
stream << map_.size() << " processes" << std::endl;
|
||||
DebugDumpLocked(stream, 0, 0);
|
||||
}
|
||||
|
||||
void ProcessTree::DebugDumpLocked(std::ostream &stream, int depth,
|
||||
pid_t ppid) const
|
||||
ABSL_SHARED_LOCKS_REQUIRED(mtx_) {
|
||||
for (auto &[_, process] : map_) {
|
||||
if ((ppid == 0 && !process->parent_) ||
|
||||
(process->parent_ && process->parent_->pid_.pid == ppid)) {
|
||||
stream << std::string(2 * depth, ' ') << process->pid_.pid
|
||||
<< process->program_->executable << std::endl;
|
||||
DebugDumpLocked(stream, depth + 1, process->pid_.pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
absl::StatusOr<std::shared_ptr<ProcessTree>> CreateTree(std::vector<std::unique_ptr<Annotator>> &&annotations) {
|
||||
absl::flat_hash_set<std::type_index> seen;
|
||||
for (const auto &annotator : annotations) {
|
||||
if (seen.count(std::type_index(typeid(annotator)))) {
|
||||
return absl::InvalidArgumentError("Multiple annotators of the same class");
|
||||
}
|
||||
seen.emplace(std::type_index(typeid(annotator)));
|
||||
}
|
||||
auto tree = std::make_shared<ProcessTree>(std::move(annotations));
|
||||
if (auto status = tree->Backfill(); !status.ok()) {
|
||||
return status;
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
/*
|
||||
----
|
||||
Tokens
|
||||
----
|
||||
*/
|
||||
|
||||
ProcessToken::ProcessToken(std::shared_ptr<ProcessTree> tree,
|
||||
std::vector<struct Pid> pids)
|
||||
: tree_(std::move(tree)), pids_(std::move(pids)) {
|
||||
tree_->RetainProcess(pids);
|
||||
}
|
||||
|
||||
ProcessToken::~ProcessToken() { tree_->ReleaseProcess(pids_); }
|
||||
|
||||
} // namespace santa::santad::process_tree
|
||||
189
Source/santad/ProcessTree/process_tree.h
Normal file
189
Source/santad/ProcessTree/process_tree.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/// Copyright 2023 Google LLC
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
#ifndef SANTA__SANTAD_PROCESSTREE_TREE_H
|
||||
#define SANTA__SANTAD_PROCESSTREE_TREE_H
|
||||
|
||||
#include <memory>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
|
||||
#include "Source/santad/ProcessTree/process.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "process.h"
|
||||
|
||||
namespace santa::santad::process_tree {
|
||||
|
||||
absl::StatusOr<Process> LoadPID(pid_t pid);
|
||||
|
||||
// Fwd decl for test peer.
|
||||
class ProcessTreeTestPeer;
|
||||
|
||||
class ProcessTree {
|
||||
public:
|
||||
explicit ProcessTree(std::vector<std::unique_ptr<Annotator>> &&annotators) : annotators_(std::move(annotators)), seen_timestamps_({}) {}
|
||||
ProcessTree(const ProcessTree &) = delete;
|
||||
ProcessTree& operator=(const ProcessTree &) = delete;
|
||||
ProcessTree(ProcessTree &&) = delete;
|
||||
ProcessTree& operator=(ProcessTree &&) = delete;
|
||||
|
||||
// Initialize the tree with the processes currently running on the system.
|
||||
absl::Status Backfill();
|
||||
|
||||
// Inform the tree of a fork event, in which the parent process spawns a child
|
||||
// with the only difference between the two being the pid.
|
||||
void HandleFork(uint64_t timestamp, const Process &parent,
|
||||
const struct Pid child);
|
||||
|
||||
// Inform the tree of an exec event, in which the program and potentially cred
|
||||
// of a Process change.
|
||||
// p is the process performing the exec (running the "old" program),
|
||||
// and new_pid, prog, and cred are the new pid, program, and credentials
|
||||
// after the exec.
|
||||
// N.B. new_pid is required as the "pid version" will have changed.
|
||||
// It is a programming error to pass a new_pid such that
|
||||
// p.pid_.pid != new_pid.pid.
|
||||
void HandleExec(uint64_t timestamp, const Process &p,
|
||||
const struct Pid new_pid, const struct Program prog,
|
||||
const struct Cred c);
|
||||
|
||||
// Inform the tree of a process exit.
|
||||
void HandleExit(uint64_t timestamp, const Process &p);
|
||||
|
||||
// Mark the given pids as needing to be retained in the tree's map for future
|
||||
// access. Normally, Processes are removed once all clients process past the
|
||||
// event which would remove the Process (e.g. exit), however in cases where
|
||||
// async processing occurs, the Process may need to be accessed after the
|
||||
// exit.
|
||||
void RetainProcess(std::vector<struct Pid> &pids);
|
||||
|
||||
// Release previously retained processes, signaling that the client is done
|
||||
// processing the event that retained them.
|
||||
void ReleaseProcess(std::vector<struct Pid> &pids);
|
||||
|
||||
// Annotate the given process with an Annotator (state).
|
||||
void AnnotateProcess(const Process &p, std::shared_ptr<const Annotator> a);
|
||||
|
||||
// Get the given annotation on the given process if it exists, or nullopt if
|
||||
// the annotation is not set.
|
||||
template <typename T>
|
||||
std::optional<std::shared_ptr<const T>> GetAnnotation(const Process &p) const;
|
||||
|
||||
// Get the fully merged proto form of all annotations on the given process.
|
||||
std::optional<::santa::pb::v1::process_tree::Annotations> ExportAnnotations(const struct Pid p);
|
||||
|
||||
// Atomically get the slice of Processes going from the given process "up"
|
||||
// to the root. The root process has no parent. N.B. There may be more than
|
||||
// one root process. E.g. on Linux, both init (PID 1) and kthread (PID 2)
|
||||
// are considered roots, as they are reported to have PPID=0.
|
||||
std::vector<std::shared_ptr<const Process>> RootSlice(
|
||||
std::shared_ptr<const Process> p) const;
|
||||
|
||||
// Call f for all processes in the tree. The list of processes is captured
|
||||
// before invoking f, so it is safe to mutate the tree in f.
|
||||
void Iterate(std::function<void(std::shared_ptr<const Process>)> f) const;
|
||||
|
||||
// Get the Process for the given pid in the tree if it exists.
|
||||
std::optional<std::shared_ptr<const Process>> Get(
|
||||
const struct Pid target) const;
|
||||
|
||||
// Traverse the tree from the given Process to its parent.
|
||||
std::shared_ptr<const Process> GetParent(const Process &p) const;
|
||||
|
||||
#if SANTA_PROCESS_TREE_DEBUG
|
||||
// Dump the tree in a human readable form to the given ostream.
|
||||
void DebugDump(std::ostream &stream) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class ProcessTreeTestPeer;
|
||||
void BackfillInsertChildren(
|
||||
absl::flat_hash_map<pid_t, std::vector<const Process>> &parent_map,
|
||||
std::shared_ptr<Process> parent, const Process &unlinked_proc);
|
||||
|
||||
// Mark that an event with the given timestamp is being processed.
|
||||
// Returns whether the given timestamp is "novel", and the tree should be
|
||||
// updated with the results of the event.
|
||||
bool Step(uint64_t timestamp);
|
||||
|
||||
std::optional<std::shared_ptr<Process>> GetLocked(
|
||||
const struct Pid target) const ABSL_SHARED_LOCKS_REQUIRED(mtx_);
|
||||
|
||||
void DebugDumpLocked(std::ostream &stream, int depth, pid_t ppid) const;
|
||||
|
||||
std::vector<std::unique_ptr<Annotator>> annotators_;
|
||||
|
||||
mutable absl::Mutex mtx_;
|
||||
absl::flat_hash_map<const struct Pid, std::shared_ptr<Process>> map_
|
||||
ABSL_GUARDED_BY(mtx_);
|
||||
// List of pids which should be removed from map_, and at the timestamp at
|
||||
// which they should be.
|
||||
// Elements are removed when the timestamp falls out of the seen_timestamps_
|
||||
// list below, signifying that all clients have synced past the timestamp.
|
||||
std::vector<std::pair<uint64_t, struct Pid>> remove_at_ ABSL_GUARDED_BY(mtx_);
|
||||
// Rolling list of event timestamps processed by the tree.
|
||||
// This is used to ensure an event only gets processed once, even if events
|
||||
// come out of order.
|
||||
std::array<uint64_t, 32> seen_timestamps_ ABSL_GUARDED_BY(mtx_);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::optional<std::shared_ptr<const T>> ProcessTree::GetAnnotation(
|
||||
const Process &p) const {
|
||||
auto it = p.annotations_.find(std::type_index(typeid(T)));
|
||||
if (it == p.annotations_.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::dynamic_pointer_cast<const T>(it->second);
|
||||
}
|
||||
|
||||
// Create a new tree, ensuring the provided annotations are valid and that backfill
|
||||
// is successful.
|
||||
absl::StatusOr<std::shared_ptr<ProcessTree>> CreateTree(std::vector<std::unique_ptr<Annotator>> &&annotations);
|
||||
|
||||
// ProcessTokens provide a lifetime based approach to retaining processes
|
||||
// in a ProcessTree. When a token is created with a list of pids that may need
|
||||
// to be referenced during processing of a given event, the ProcessToken informs
|
||||
// the tree to retain those pids in its map so any call to ProcessTree::Get()
|
||||
// during event processing succeeds. When the token is destroyed, it signals the
|
||||
// tree to release the pids, which removes them from the tree if they would have
|
||||
// fallen out otherwise due to a destruction event (e.g. exit).
|
||||
class ProcessToken {
|
||||
public:
|
||||
explicit ProcessToken(std::shared_ptr<ProcessTree> tree,
|
||||
std::vector<struct Pid> pids);
|
||||
~ProcessToken();
|
||||
ProcessToken(const ProcessToken &other)
|
||||
: ProcessToken(other.tree_, other.pids_) {}
|
||||
ProcessToken(ProcessToken &&other) noexcept
|
||||
: tree_(std::move(other.tree_)), pids_(std::move(other.pids_)) {}
|
||||
ProcessToken &operator=(const ProcessToken &other) {
|
||||
return *this = ProcessToken(other.tree_, other.pids_);
|
||||
}
|
||||
ProcessToken &operator=(ProcessToken &&other) noexcept {
|
||||
tree_ = std::move(other.tree_);
|
||||
pids_ = std::move(other.pids_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ProcessTree> tree_;
|
||||
std::vector<struct Pid> pids_;
|
||||
};
|
||||
|
||||
} // namespace santa::santad::process_tree
|
||||
|
||||
#endif
|
||||
6
Source/santad/ProcessTree/process_tree.proto
Normal file
6
Source/santad/ProcessTree/process_tree.proto
Normal file
@@ -0,0 +1,6 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package santa.pb.v1.process_tree;
|
||||
|
||||
message Annotations {
|
||||
}
|
||||
78
Source/santad/ProcessTree/process_tree_macos.mm
Normal file
78
Source/santad/ProcessTree/process_tree_macos.mm
Normal file
@@ -0,0 +1,78 @@
|
||||
/// Copyright 2023 Google LLC
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
#include "Source/santad/ProcessTree/process_tree.h"
|
||||
|
||||
#include <libproc.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "Source/santad/ProcessTree/process.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
|
||||
namespace santa::santad::process_tree {
|
||||
|
||||
absl::StatusOr<Process> LoadPID(pid_t pid) {
|
||||
// TODO
|
||||
return absl::UnimplementedError("LoadPID not implemented");
|
||||
}
|
||||
|
||||
absl::Status ProcessTree::Backfill() {
|
||||
int n_procs = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
|
||||
if (n_procs < 0) {
|
||||
return absl::InternalError("proc_listpids failed");
|
||||
}
|
||||
n_procs /= sizeof(pid_t);
|
||||
|
||||
std::vector<pid_t> pids;
|
||||
pids.resize(n_procs + 16); // add space for a few more processes
|
||||
// in case some spawn in-between.
|
||||
|
||||
n_procs = proc_listpids(PROC_ALL_PIDS, 0, pids.data(), (int)(pids.size() * sizeof(pid_t)));
|
||||
if (n_procs < 0) {
|
||||
return absl::InternalError("proc_listpids failed");
|
||||
}
|
||||
n_procs /= sizeof(pid_t);
|
||||
pids.resize(n_procs);
|
||||
|
||||
absl::flat_hash_map<pid_t, std::vector<const Process>> parent_map;
|
||||
for (pid_t pid : pids) {
|
||||
auto proc_status = LoadPID(pid);
|
||||
if (proc_status.ok()) {
|
||||
auto unlinked_proc = proc_status.value();
|
||||
|
||||
// Determine ppid
|
||||
// Alternatively, there's a sysctl interface:
|
||||
// https://chromium.googlesource.com/chromium/chromium/+/master/base/process_util_openbsd.cc#32
|
||||
struct proc_bsdinfo bsdinfo;
|
||||
if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &bsdinfo, sizeof(bsdinfo)) !=
|
||||
PROC_PIDTBSDINFO_SIZE) {
|
||||
continue;
|
||||
};
|
||||
|
||||
parent_map[bsdinfo.pbi_ppid].push_back(unlinked_proc);
|
||||
}
|
||||
}
|
||||
|
||||
auto &roots = parent_map[0];
|
||||
for (const Process &p : roots) {
|
||||
BackfillInsertChildren(parent_map, std::shared_ptr<Process>(), p);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace santa::santad::process_tree
|
||||
220
Source/santad/ProcessTree/process_tree_test.mm
Normal file
220
Source/santad/ProcessTree/process_tree_test.mm
Normal file
@@ -0,0 +1,220 @@
|
||||
/// Copyright 2023 Google LLC
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include <bsm/libbsm.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "Source/santad/ProcessTree/annotations/annotator.h"
|
||||
#include "Source/santad/ProcessTree/process.h"
|
||||
#include "Source/santad/ProcessTree/process_tree_test_helpers.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
|
||||
namespace ptpb = ::santa::pb::v1::process_tree;
|
||||
|
||||
namespace santa::santad::process_tree {
|
||||
|
||||
static constexpr std::string_view kAnnotatedExecutable = "/usr/bin/login";
|
||||
|
||||
class TestAnnotator : public Annotator {
|
||||
public:
|
||||
TestAnnotator() {}
|
||||
void AnnotateFork(ProcessTree &tree, const Process &parent, const Process &child) override;
|
||||
void AnnotateExec(ProcessTree &tree, const Process &orig_process,
|
||||
const Process &new_process) override;
|
||||
std::optional<::ptpb::Annotations> Proto() const override;
|
||||
};
|
||||
|
||||
void TestAnnotator::AnnotateFork(ProcessTree &tree, const Process &parent, const Process &child) {
|
||||
// "Base case". Propagate existing annotations down to descendants.
|
||||
if (auto annotation = tree.GetAnnotation<TestAnnotator>(parent)) {
|
||||
tree.AnnotateProcess(child, std::move(*annotation));
|
||||
}
|
||||
}
|
||||
|
||||
void TestAnnotator::AnnotateExec(ProcessTree &tree, const Process &orig_process,
|
||||
const Process &new_process) {
|
||||
if (auto annotation = tree.GetAnnotation<TestAnnotator>(orig_process)) {
|
||||
tree.AnnotateProcess(new_process, std::move(*annotation));
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_process.program_->executable == kAnnotatedExecutable) {
|
||||
tree.AnnotateProcess(new_process, std::make_shared<TestAnnotator>());
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<::ptpb::Annotations> TestAnnotator::Proto() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace santa::santad::process_tree
|
||||
|
||||
using namespace santa::santad::process_tree;
|
||||
|
||||
@interface ProcessTreeTest : XCTestCase
|
||||
@property std::shared_ptr<ProcessTreeTestPeer> tree;
|
||||
@property std::shared_ptr<const Process> init_proc;
|
||||
@end
|
||||
|
||||
@implementation ProcessTreeTest
|
||||
|
||||
- (void)setUp {
|
||||
std::vector<std::unique_ptr<Annotator>> annotators{};
|
||||
self.tree = std::make_shared<ProcessTreeTestPeer>(std::move(annotators));
|
||||
self.init_proc = self.tree->InsertInit();
|
||||
}
|
||||
|
||||
- (void)testSimpleOps {
|
||||
uint64_t event_id = 1;
|
||||
// PID 1.1: fork() -> PID 2.2
|
||||
const struct Pid child_pid = {.pid = 2, .pidversion = 2};
|
||||
self.tree->HandleFork(event_id++, *self.init_proc, child_pid);
|
||||
|
||||
auto child_opt = self.tree->Get(child_pid);
|
||||
XCTAssertTrue(child_opt.has_value());
|
||||
std::shared_ptr<const Process> child = *child_opt;
|
||||
XCTAssertEqual(child->pid_, child_pid);
|
||||
XCTAssertEqual(child->program_, self.init_proc->program_);
|
||||
XCTAssertEqual(child->effective_cred_, self.init_proc->effective_cred_);
|
||||
XCTAssertEqual(self.tree->GetParent(*child), self.init_proc);
|
||||
|
||||
// PID 2.2: exec("/bin/bash") -> PID 2.3
|
||||
const struct Pid child_exec_pid = {.pid = 2, .pidversion = 3};
|
||||
const struct Program child_exec_prog = {.executable = "/bin/bash",
|
||||
.arguments = {"/bin/bash", "-i"}};
|
||||
self.tree->HandleExec(event_id++, *child, child_exec_pid, child_exec_prog,
|
||||
child->effective_cred_);
|
||||
|
||||
child_opt = self.tree->Get(child_exec_pid);
|
||||
XCTAssertTrue(child_opt.has_value());
|
||||
child = *child_opt;
|
||||
XCTAssertEqual(child->pid_, child_exec_pid);
|
||||
XCTAssertEqual(*child->program_, child_exec_prog);
|
||||
XCTAssertEqual(child->effective_cred_, self.init_proc->effective_cred_);
|
||||
}
|
||||
|
||||
- (void)testAnnotation {
|
||||
std::vector<std::unique_ptr<Annotator>> annotators{};
|
||||
annotators.emplace_back(std::make_unique<TestAnnotator>());
|
||||
self.tree = std::make_shared<ProcessTreeTestPeer>(std::move(annotators));
|
||||
self.init_proc = self.tree->InsertInit();
|
||||
|
||||
uint64_t event_id = 1;
|
||||
const struct Cred cred = {.uid = 0, .gid = 0};
|
||||
|
||||
// PID 1.1: fork() -> PID 2.2
|
||||
const struct Pid login_pid = {.pid = 2, .pidversion = 2};
|
||||
self.tree->HandleFork(event_id++, *self.init_proc, login_pid);
|
||||
|
||||
// PID 2.2: exec("/usr/bin/login") -> PID 2.3
|
||||
const struct Pid login_exec_pid = {.pid = 2, .pidversion = 3};
|
||||
const struct Program login_prog = {.executable = std::string(kAnnotatedExecutable),
|
||||
.arguments = {}};
|
||||
auto login = *self.tree->Get(login_pid);
|
||||
self.tree->HandleExec(event_id++, *login, login_exec_pid, login_prog, cred);
|
||||
|
||||
// Ensure we have an annotation on login itself...
|
||||
login = *self.tree->Get(login_exec_pid);
|
||||
auto annotation = self.tree->GetAnnotation<TestAnnotator>(*login);
|
||||
XCTAssertTrue(annotation.has_value());
|
||||
|
||||
// PID 2.3: fork() -> PID 3.3
|
||||
const struct Pid shell_pid = {.pid = 3, .pidversion = 3};
|
||||
self.tree->HandleFork(event_id++, *login, shell_pid);
|
||||
// PID 3.3: exec("/bin/zsh") -> PID 3.4
|
||||
const struct Pid shell_exec_pid = {.pid = 3, .pidversion = 4};
|
||||
const struct Program shell_prog = {.executable = "/bin/zsh", .arguments = {}};
|
||||
auto shell = *self.tree->Get(shell_pid);
|
||||
self.tree->HandleExec(event_id++, *shell, shell_exec_pid, shell_prog, cred);
|
||||
|
||||
// ... and also ensure we have an annotation on the descendant zsh.
|
||||
shell = *self.tree->Get(shell_exec_pid);
|
||||
annotation = self.tree->GetAnnotation<TestAnnotator>(*shell);
|
||||
XCTAssertTrue(annotation.has_value());
|
||||
}
|
||||
|
||||
- (void)testCleanup {
|
||||
uint64_t event_id = 1;
|
||||
const struct Pid child_pid = {.pid = 2, .pidversion = 2};
|
||||
{
|
||||
self.tree->HandleFork(event_id++, *self.init_proc, child_pid);
|
||||
auto child = *self.tree->Get(child_pid);
|
||||
self.tree->HandleExit(event_id++, *child);
|
||||
}
|
||||
|
||||
// We should still be able to get a handle to child...
|
||||
{
|
||||
auto child = self.tree->Get(child_pid);
|
||||
XCTAssertTrue(child.has_value());
|
||||
}
|
||||
|
||||
// ... until we step far enough into the future (32 events).
|
||||
struct Pid churn_pid = {.pid = 3, .pidversion = 3};
|
||||
for (int i = 0; i < 32; i++) {
|
||||
self.tree->HandleFork(event_id++, *self.init_proc, churn_pid);
|
||||
churn_pid.pid++;
|
||||
}
|
||||
|
||||
// Now when we try processing the next event, it should have fallen out of the tree.
|
||||
self.tree->HandleFork(event_id++, *self.init_proc, churn_pid);
|
||||
{
|
||||
auto child = self.tree->Get(child_pid);
|
||||
XCTAssertFalse(child.has_value());
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testRefcountCleanup {
|
||||
uint64_t event_id = 1;
|
||||
const struct Pid child_pid = {.pid = 2, .pidversion = 2};
|
||||
{
|
||||
self.tree->HandleFork(event_id++, *self.init_proc, child_pid);
|
||||
auto child = *self.tree->Get(child_pid);
|
||||
self.tree->HandleExit(event_id++, *child);
|
||||
}
|
||||
|
||||
{
|
||||
auto child = self.tree->Get(child_pid);
|
||||
XCTAssertTrue(child.has_value());
|
||||
std::vector<struct Pid> pids = {(*child)->pid_};
|
||||
self.tree->RetainProcess(pids);
|
||||
}
|
||||
|
||||
// Even if we step far into the future, we should still be able to lookup
|
||||
// the child.
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
struct Pid churn_pid = {.pid = 100 + i, .pidversion = (uint64_t)(100 + i)};
|
||||
self.tree->HandleFork(event_id++, *self.init_proc, churn_pid);
|
||||
auto child = self.tree->Get(child_pid);
|
||||
XCTAssertTrue(child.has_value());
|
||||
}
|
||||
|
||||
// But when released...
|
||||
{
|
||||
auto child = self.tree->Get(child_pid);
|
||||
XCTAssertTrue(child.has_value());
|
||||
std::vector<struct Pid> pids = {(*child)->pid_};
|
||||
self.tree->ReleaseProcess(pids);
|
||||
}
|
||||
|
||||
// ... it should immediately be removed.
|
||||
{
|
||||
auto child = self.tree->Get(child_pid);
|
||||
XCTAssertFalse(child.has_value());
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
30
Source/santad/ProcessTree/process_tree_test_helpers.h
Normal file
30
Source/santad/ProcessTree/process_tree_test_helpers.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/// Copyright 2023 Google LLC
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
#ifndef SANTA__SANTAD_PROCESSTREE_TREE_TEST_HELPERS_H
|
||||
#define SANTA__SANTAD_PROCESSTREE_TREE_TEST_HELPERS_H
|
||||
#include <memory>
|
||||
|
||||
#include "Source/santad/ProcessTree/process_tree.h"
|
||||
|
||||
namespace santa::santad::process_tree {
|
||||
|
||||
class ProcessTreeTestPeer : public ProcessTree {
|
||||
public:
|
||||
explicit ProcessTreeTestPeer(std::vector<std::unique_ptr<Annotator>> &&annotators) : ProcessTree(std::move(annotators)) {}
|
||||
std::shared_ptr<const Process> InsertInit();
|
||||
};
|
||||
|
||||
} // namespace santa::santad::process_tree
|
||||
|
||||
#endif
|
||||
42
Source/santad/ProcessTree/process_tree_test_helpers.mm
Normal file
42
Source/santad/ProcessTree/process_tree_test_helpers.mm
Normal file
@@ -0,0 +1,42 @@
|
||||
/// Copyright 2023 Google LLC
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "Source/santad/ProcessTree/process.h"
|
||||
#include "Source/santad/ProcessTree/process_tree.h"
|
||||
|
||||
namespace santa::santad::process_tree {
|
||||
|
||||
class ProcessTreeTestPeer : public ProcessTree {
|
||||
public:
|
||||
std::shared_ptr<const Process> InsertInit();
|
||||
};
|
||||
|
||||
std::shared_ptr<const Process> ProcessTreeTestPeer::InsertInit() {
|
||||
absl::MutexLock lock(&mtx_);
|
||||
struct Pid initpid = {
|
||||
.pid = 1,
|
||||
.pidversion = 1,
|
||||
};
|
||||
auto proc = std::make_shared<Process>(
|
||||
initpid, (Cred){.uid = 0, .gid = 0},
|
||||
std::make_shared<Program>((Program){.executable = "/init", .arguments = {"/init"}}), nullptr);
|
||||
map_.emplace(initpid, proc);
|
||||
return proc;
|
||||
}
|
||||
|
||||
} // namespace santa::santad::process_tree
|
||||
Reference in New Issue
Block a user