blob: c5ffa279f659700034df1fb1abdd6f7a61b21ca8 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <bpf/libbpf.h>
#include <bpf/libbpf_legacy.h>
#include <string.h>
#include <utility>
#include "absl/status/status.h"
#include "base/strings/strcat.h"
#include "secagentd/bpf_skeleton_wrappers.h"
#include "secagentd/bpf_skeletons/skeleton_process_bpf.h"
#include "secagentd/bpf_utils.h"
#include "secagentd/metrics_sender.h"
namespace secagentd {
ProcessBpfSkeleton::~ProcessBpfSkeleton() {
// File Controller documentation states that the file descriptor being watched
// must outlive the controller. In this case rb_watch_readable_ is watching
// the ring buffer file descriptor so force its destruction before we close
// the file descriptor.
rb_watch_readable_ = nullptr;
if (rb_ != nullptr) {
// This frees and also closes all ring buffer file descriptors.
ring_buffer__free(rb_);
}
if (skel_ != nullptr) {
process_bpf__destroy(skel_);
}
}
void ProcessBpfSkeleton::RegisterCallbacks(BpfCallbacks cbs) {
callbacks_ = cbs;
}
int ProcessBpfSkeleton::ConsumeEvent() {
if (rb_ == nullptr) {
return -1;
}
return ring_buffer__consume(rb_);
}
absl::Status ProcessBpfSkeleton::LoadAndAttach() {
if (callbacks_.ring_buffer_event_callback.is_null() ||
callbacks_.ring_buffer_read_ready_callback.is_null()) {
return absl::InternalError(
"ProcessBPF: LoadAndAttach failed, one or more provided callbacks "
"are null.");
}
libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
#if defined(USE_MIN_CORE_BTF) && USE_MIN_CORE_BTF == 1
// Ask libbpf to load a BTF that's tailored specifically to this BPF. Note
// that this is more of a suggestion because libbpf will silently ignore the
// request if it doesn't like the type of BPF or its access patterns.
const std::string btf_path =
base::StrCat({secagentd::kMinCoreBtfDir, "process_bpf.min.btf"});
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, open_opts,
.btf_custom_path = btf_path.c_str());
skel_ = process_bpf__open_opts(&open_opts);
#else
// Let libbpf extract BTF from /sys/kernel/btf/vmlinux.
skel_ = process_bpf__open();
#endif // USE_MIN_CORE_BTF
if (!skel_) {
MetricsSender::GetInstance().SendEnumMetricToUMA(
metrics::kProcessBpfAttach, metrics::BpfAttachResult::kErrorOpen);
return absl::InternalError("BPF skeleton failed to open.");
}
if (process_bpf__load(skel_)) {
MetricsSender::GetInstance().SendEnumMetricToUMA(
metrics::kProcessBpfAttach, metrics::BpfAttachResult::kErrorLoad);
return absl::InternalError(
"ProcessBPF: application failed loading and verification.");
}
if (process_bpf__attach(skel_)) {
MetricsSender::GetInstance().SendEnumMetricToUMA(
metrics::kProcessBpfAttach, metrics::BpfAttachResult::kErrorLoad);
return absl::InternalError("ProcessBPF: program failed to attach.");
}
int map_fd = bpf_map__fd(skel_->maps.rb);
int epoll_fd{-1};
// ring_buffer__new will fail with an invalid fd but we explicitly check
// anyways for code clarity.
if (map_fd >= 0) {
rb_ = ring_buffer__new(
map_fd, indirect_c_callback,
static_cast<void*>(&callbacks_.ring_buffer_event_callback), nullptr);
epoll_fd = ring_buffer__epoll_fd(rb_);
}
if (map_fd < 0 || !rb_ || epoll_fd < 0) {
MetricsSender::GetInstance().SendEnumMetricToUMA(
metrics::kProcessBpfAttach, metrics::BpfAttachResult::kErrorRingBuffer);
return absl::InternalError("ProcessBPF: Ring buffer creation failed.");
}
rb_watch_readable_ = base::FileDescriptorWatcher::WatchReadable(
epoll_fd, callbacks_.ring_buffer_read_ready_callback);
MetricsSender::GetInstance().SendEnumMetricToUMA(
metrics::kProcessBpfAttach, metrics::BpfAttachResult::kSuccess);
return absl::OkStatus();
}
} // namespace secagentd