Remove unused/experimental ebpf code (#6879)

This commit is contained in:
Stefano Bonicatti 2021-01-01 21:46:00 +01:00 committed by GitHub
parent 4995a238c5
commit 7cbc19038e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 0 additions and 3338 deletions

View File

@ -6,4 +6,3 @@
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
add_subdirectory("events_stream")
add_subdirectory("tracing")

View File

@ -1,55 +0,0 @@
# Copyright (c) 2014-present, The osquery authors
#
# This source code is licensed as defined by the LICENSE file found in the
# root directory of this source tree.
#
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
function(osqueryExperimentalTracingMain)
add_subdirectory("linux")
generateOsqueryExperimentalTracingSyscallstracing()
endfunction()
function(generateOsqueryExperimentalTracingSyscallstracing)
set(source_files
syscalls_tracing.cpp
)
if(DEFINED PLATFORM_LINUX)
list(APPEND source_files
syscalls_tracing_impl.cpp
)
endif()
add_osquery_library(osquery_experimental_tracing_syscallstracing EXCLUDE_FROM_ALL ${source_files})
target_link_libraries(osquery_experimental_tracing_syscallstracing PUBLIC
osquery_cxx_settings
osquery_core
osquery_dispatcher
osquery_experimental_tracing_linux_probesevents
osquery_experimental_eventsstream
osquery_utils_caches_lru
osquery_utils_conversions
osquery_utils_json
osquery_utils_system_linux_proc
osquery_utils_system_time
)
set(public_header_files
syscalls_tracing.h
)
if(DEFINED PLATFORM_LINUX)
list(APPEND public_header_files
syscalls_tracing_impl.h
)
endif()
generateIncludeNamespace(osquery_experimental_tracing_syscallstracing "osquery/experimental/tracing" "FILE_ONLY" ${public_header_files})
endfunction()
osqueryExperimentalTracingMain()

View File

@ -1,56 +0,0 @@
# Copyright (c) 2014-present, The osquery authors
#
# This source code is licensed as defined by the LICENSE file found in the
# root directory of this source tree.
#
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
function(osqueryExperimentalTracingLinuxMain)
if(OSQUERY_BUILD_TESTS)
add_subdirectory("tests")
endif()
generateExperimentalTracingLinuxProbesevents()
endfunction()
function(generateExperimentalTracingLinuxProbesevents)
set(library_name "osquery_experimental_tracing_linux_probesevents")
if(DEFINED PLATFORM_LINUX)
set(source_files
ebpf_tracepoint.cpp
probes.cpp
syscall_event.cpp
syscalls_programs.cpp
)
add_osquery_library("${library_name}" EXCLUDE_FROM_ALL ${source_files})
target_link_libraries("${library_name}" PUBLIC
osquery_cxx_settings
osquery_logger
osquery_utils_conversions
osquery_utils_expected
osquery_utils_system_linux_ebpf
osquery_utils_system_linux_tracing
osquery_utils
thirdparty_boost
)
set(public_header_files
ebpf_tracepoint.h
probes.h
syscall_event.h
syscalls_programs.h
)
generateIncludeNamespace("${library_name}" "osquery/experimental/tracing/linux" "FILE_ONLY" ${public_header_files})
add_test(NAME osquery_experimental_tracing_linux_tests_ebpftracepointtests-test COMMAND osquery_experimental_tracing_linux_tests_ebpftracepointtests-test)
else()
add_osquery_library("${library_name}" INTERFACE)
endif()
endfunction()
osqueryExperimentalTracingLinuxMain()

View File

@ -1,122 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/experimental/tracing/linux/ebpf_tracepoint.h>
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/system/linux/perf_event/perf_event.h>
#include <osquery/logger/logger.h>
#include <boost/io/detail/quoted_manip.hpp>
#include <sys/ioctl.h>
namespace osquery {
namespace events {
EbpfTracepoint::EbpfTracepoint(tracing::NativeEvent system_event,
ebpf::Program program)
: system_event_{std::move(system_event)}, program_{std::move(program)} {}
EbpfTracepoint::EbpfTracepoint(EbpfTracepoint&& other)
: fd_{other.fd_},
system_event_{std::move(other.system_event_)},
program_{std::move(other.program_)} {
other.fd_ = -1;
}
EbpfTracepoint& EbpfTracepoint::operator=(EbpfTracepoint&& other) {
std::swap(system_event_, other.system_event_);
std::swap(program_, other.program_);
std::swap(fd_, other.fd_);
return *this;
}
EbpfTracepoint::~EbpfTracepoint() {
forceUnload();
}
Expected<EbpfTracepoint, EbpfTracepoint::Error> EbpfTracepoint::load(
tracing::NativeEvent system_event, ebpf::Program program) {
auto instance = EbpfTracepoint(std::move(system_event), std::move(program));
struct perf_event_attr trace_attr;
memset(&trace_attr, 0, sizeof(struct perf_event_attr));
trace_attr.type = PERF_TYPE_TRACEPOINT;
trace_attr.size = sizeof(struct perf_event_attr);
trace_attr.config = instance.system_event_.id();
trace_attr.sample_period = 1;
trace_attr.sample_type = PERF_SAMPLE_RAW;
trace_attr.wakeup_events = 1;
trace_attr.disabled = 1;
pid_t const pid = -1;
int const cpu = 0;
int const group_fd = -1;
unsigned long const flags = PERF_FLAG_FD_CLOEXEC;
auto fd_exp =
perf_event_open::syscall(&trace_attr, pid, cpu, group_fd, flags);
if (fd_exp.isError()) {
return createError(Error::SystemError, fd_exp.takeError())
<< "Fail to create perf_event tracepoint";
}
instance.fd_ = fd_exp.take();
if (ioctl(instance.fd_, PERF_EVENT_IOC_SET_BPF, instance.program_.fd()) < 0) {
return createError(Error::SystemError)
<< "Fail to attach perf event of EbpfTracepoint "
<< boost::io::quoted(strerror(errno));
}
if (ioctl(instance.fd_, PERF_EVENT_IOC_ENABLE, 0) < 0) {
return createError(Error::SystemError)
<< "Fail to enable perf event of EbpfTracepoint "
<< boost::io::quoted(strerror(errno));
}
return std::move(instance);
}
ExpectedSuccess<EbpfTracepoint::Error> EbpfTracepoint::unload() {
if (fd_ < 0) {
return Success{};
}
bool failed = false;
std::string err_msg;
int ret = ioctl(fd_, PERF_EVENT_IOC_DISABLE, 0);
if (ret < 0) {
failed = true;
err_msg += " perf event disabling failed: \"";
err_msg += strerror(errno);
err_msg += "\". ";
}
ret = close(fd_);
if (ret < 0) {
failed = true;
err_msg += " file descriptor closed with error: \"";
err_msg += strerror(errno);
err_msg += "\".";
}
fd_ = -1;
if (failed) {
return createError(Error::SystemError)
<< "EbpfTracepoint unload failed " << err_msg;
}
return Success{};
}
void EbpfTracepoint::forceUnload() {
auto const exp = unload();
if (exp.isError()) {
LOG(ERROR) << "Could not unload perf tracepoint "
<< boost::io::quoted(exp.getError().getMessage());
}
}
} // namespace events
} // namespace osquery

View File

@ -1,54 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/utils/system/linux/ebpf/program.h>
#include <osquery/utils/system/linux/tracing/native_event.h>
#include <osquery/utils/expected/expected.h>
namespace osquery {
namespace events {
class EbpfTracepoint final {
public:
EbpfTracepoint(EbpfTracepoint&&);
EbpfTracepoint& operator=(EbpfTracepoint&&);
EbpfTracepoint(EbpfTracepoint const&) = delete;
EbpfTracepoint& operator=(EbpfTracepoint const&) = delete;
enum class Error {
Unknown = 1,
SystemError = 2,
};
~EbpfTracepoint();
static Expected<EbpfTracepoint, Error> load(tracing::NativeEvent system_event,
ebpf::Program program);
private:
explicit EbpfTracepoint(tracing::NativeEvent system_event,
ebpf::Program program);
ExpectedSuccess<Error> unload();
void forceUnload();
private:
int fd_ = -1;
tracing::NativeEvent system_event_;
ebpf::Program program_;
};
} // namespace events
} // namespace osquery

View File

@ -1,189 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/experimental/tracing/linux/probes.h>
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/map_take.h>
#include <osquery/utils/system/linux/cpu.h>
#include <osquery/utils/system/linux/perf_event/perf_event.h>
#include <osquery/utils/system/posix/errno.h>
#include <osquery/logger/logger.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <sys/time.h>
namespace osquery {
namespace events {
namespace {
Expected<std::string, LinuxProbesControl::Error> toTracingPath(
syscall::EventType type) {
static const auto table =
std::unordered_map<syscall::EventType, std::string, EnumClassHash>{
{syscall::EventType::KillEnter, "syscalls/sys_enter_kill"},
{syscall::EventType::KillExit, "syscalls/sys_exit_kill"},
{syscall::EventType::SetuidEnter, "syscalls/sys_enter_setuid"},
{syscall::EventType::SetuidExit, "syscalls/sys_exit_setuid"},
};
auto exp = tryTakeCopy(table, type);
if (exp.isError()) {
return createError(LinuxProbesControl::Error::InvalidArgument,
exp.takeError())
<< "unknown tracing event path for type " << to<std::string>(type);
}
return exp.take();
}
size_t constexpr kMemoryLockSize = 266240u;
ExpectedSuccess<PosixError> setMemoryLockSystemLimit() {
struct rlimit limits = {RLIM_INFINITY, RLIM_INFINITY};
auto ret = setrlimit(RLIMIT_MEMLOCK, &limits);
if (ret < 0) {
return createError(to<PosixError>(errno))
<< "setrlimit() syscall failed: "
<< boost::io::quoted(strerror(errno));
}
return Success{};
}
} // namespace
LinuxProbesControl::LinuxProbesControl(
PerfEventCpuMap cpu_to_perf_output_map,
ebpf::PerfOutputsPoll<events::syscall::Event> output_poll)
: cpu_to_perf_output_map_(std::move(cpu_to_perf_output_map)),
output_poll_(std::move(output_poll)) {}
Expected<LinuxProbesControl, LinuxProbesControl::Error>
LinuxProbesControl::spawn() {
auto exp = setMemoryLockSystemLimit();
if (exp.isError()) {
return createError(Error::SystemUnknown, exp.takeError())
<< "failed to set appropriate memory lock limits";
}
auto cpu_map_exp =
ebpf::createMap<int, int, BPF_MAP_TYPE_PERF_EVENT_ARRAY>(cpu::kMaskSize);
if (cpu_map_exp.isError()) {
return createError(Error::SystemEbpf, cpu_map_exp.takeError())
<< "failed to create eBPF map for {cpu -> perf} table";
}
auto cpu_map = cpu_map_exp.take();
auto output_poll = ebpf::PerfOutputsPoll<events::syscall::Event>{};
auto online_cpu_exp = cpu::getOnline();
if (online_cpu_exp.isError()) {
return createError(Error::SystemUnknown, online_cpu_exp.takeError())
<< "failed to load cpu configuration";
}
auto const online_cpu = online_cpu_exp.take();
for (auto cpu_i = std::size_t{0}; cpu_i < online_cpu.size(); ++cpu_i) {
if (online_cpu.test(cpu_i)) {
auto output_exp = ebpf::PerfOutput<events::syscall::Event>::load(
cpu_i, kMemoryLockSize);
if (output_exp.isError()) {
return createError(Error::SystemPerfEvent, output_exp.takeError())
<< "perf events output initialisation failed";
}
{
auto status = cpu_map.updateElement(cpu_i, output_exp->fd());
if (status.isError()) {
return createError(Error::SystemEbpf, status.takeError())
<< "loading perf events output to map failed";
}
}
{
auto status = output_poll.add(output_exp.take());
if (status.isError()) {
return createError(Error::SystemUnknown, status.takeError())
<< "adding new output to PerfOutputsPoll failed";
}
}
}
}
return LinuxProbesControl(std::move(cpu_map), std::move(output_poll));
}
ebpf::PerfOutputsPoll<events::syscall::Event>& LinuxProbesControl::getReader() {
return output_poll_;
}
namespace {
Expected<EbpfTracepoint, LinuxProbesControl::Error> createTracepointForSyscall(
syscall::EventType type, PerfEventCpuMap const& cpu_map) {
auto program_exp = genLinuxProgram(BPF_PROG_TYPE_TRACEPOINT, cpu_map, type);
if (program_exp.isError()) {
return createError(LinuxProbesControl::Error::SystemEbpf,
program_exp.takeError())
<< "could not load program to track syscall "
<< to<std::string>(type);
}
auto tracing_path_exp = toTracingPath(type);
if (tracing_path_exp.isError()) {
return createError(LinuxProbesControl::Error::InvalidArgument,
tracing_path_exp.takeError());
}
auto sys_event_exp = tracing::NativeEvent::load(tracing_path_exp.take());
if (sys_event_exp.isError()) {
return createError(LinuxProbesControl::Error::SystemNativeEvent,
sys_event_exp.takeError())
<< "could not enable linux event for " << to<std::string>(type);
}
auto tracepoint_exp =
events::EbpfTracepoint::load(sys_event_exp.take(), program_exp.take());
if (tracepoint_exp.isError()) {
return createError(LinuxProbesControl::Error::SystemTracepoint,
tracepoint_exp.takeError())
<< "could not attach tracing prograp to the native event of "
<< to<std::string>(type);
}
return tracepoint_exp.take();
}
} // namespace
ExpectedSuccess<LinuxProbesControl::Error>
LinuxProbesControl::traceEnterAndExit(syscall::EventType type) {
if (type == syscall::EventType::Unknown) {
return createError(Error::InvalidArgument)
<< "Wrong syscall type: 'Unknown'";
}
auto tracepoint_exp =
createTracepointForSyscall(type, cpu_to_perf_output_map_);
if (tracepoint_exp.isValue()) {
auto const inv_type = syscall::flipEventType(type);
auto inv_tracepoint_exp =
createTracepointForSyscall(inv_type, cpu_to_perf_output_map_);
if (inv_tracepoint_exp.isValue()) {
probes_.emplace(type, tracepoint_exp.take());
probes_.emplace(inv_type, inv_tracepoint_exp.take());
return Success{};
} else {
return inv_tracepoint_exp.takeError();
}
}
return tracepoint_exp.takeError();
}
ExpectedSuccess<LinuxProbesControl::Error> LinuxProbesControl::traceKill() {
return traceEnterAndExit(syscall::EventType::KillEnter);
}
ExpectedSuccess<LinuxProbesControl::Error> LinuxProbesControl::traceSetuid() {
return traceEnterAndExit(syscall::EventType::SetuidEnter);
}
} // namespace events
} // namespace osquery

View File

@ -1,57 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/experimental/tracing/linux/ebpf_tracepoint.h>
#include <osquery/experimental/tracing/linux/syscall_event.h>
#include <osquery/experimental/tracing/linux/syscalls_programs.h>
#include <osquery/utils/enum_class_hash.h>
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/system/linux/ebpf/perf_output.h>
namespace osquery {
namespace events {
class LinuxProbesControl final {
public:
enum class Error {
SystemUnknown = 1,
SystemEbpf = 2,
SystemNativeEvent = 3,
SystemTracepoint = 4,
SystemPerfEvent = 5,
InvalidArgument = 6,
};
static Expected<LinuxProbesControl, LinuxProbesControl::Error> spawn();
ebpf::PerfOutputsPoll<events::syscall::Event>& getReader();
ExpectedSuccess<Error> traceKill();
ExpectedSuccess<Error> traceSetuid();
private:
using PerfEventCpuMap = ebpf::Map<int, int, BPF_MAP_TYPE_PERF_EVENT_ARRAY>;
explicit LinuxProbesControl(
PerfEventCpuMap cpu_to_perf_output_map,
ebpf::PerfOutputsPoll<events::syscall::Event> output_poll);
ExpectedSuccess<Error> traceEnterAndExit(syscall::EventType type);
private:
std::unordered_map<syscall::EventType, EbpfTracepoint, EnumClassHash> probes_;
PerfEventCpuMap cpu_to_perf_output_map_;
ebpf::PerfOutputsPoll<events::syscall::Event> output_poll_;
};
} // namespace events
} // namespace osquery

View File

@ -1,82 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/experimental/tracing/linux/syscall_event.h>
namespace osquery {
namespace events {
namespace syscall {
namespace {
static constexpr EnterExitJoiner::CounterType kCounterLimit = 256;
EnterExitJoiner::KeyType createKey(EventType const type,
__s32 const pid,
__s32 const tgid) {
auto key = EnterExitJoiner::KeyType(static_cast<std::uint32_t>(pid));
key <<= 32;
key |= static_cast<std::uint32_t>(tgid);
key <<= 32;
key |= static_cast<std::uint32_t>(type);
return key;
}
} // namespace
boost::optional<Event> EnterExitJoiner::join(Event in_event) {
++counter_;
if (counter_ > kCounterLimit) {
drop_stuck_events();
counter_ = 0;
}
auto const inv_key =
createKey(flipEventType(in_event.type), in_event.pid, in_event.tgid);
auto it = table_.find(inv_key);
if (it == table_.end()) {
auto const key = createKey(in_event.type, in_event.pid, in_event.tgid);
// As far as `return_value` is not used while the event is in the table_
// we can use it to preserve the counter value as the age of the event.
in_event.return_value = counter_;
table_.emplace(key, std::move(in_event));
return boost::none;
}
if (isEventTypeExit(in_event.type)) {
auto enter = std::move(it->second);
enter.return_value = in_event.body.exit.ret;
table_.erase(it);
return enter;
}
in_event.return_value = it->second.body.exit.ret;
table_.erase(it);
return in_event;
}
bool EnterExitJoiner::isEmpty() const {
return table_.empty();
}
void EnterExitJoiner::drop_stuck_events() {
// As far as `table_` is relatively small we can afford to iterarte over it
// once in a kCounterLimit events in order to clean it up.
for (auto it = table_.begin(); it != table_.end();) {
if (it->second.return_value < 0) {
it = table_.erase(it);
} else {
it->second.return_value -= kCounterLimit;
++it;
}
}
}
} // namespace syscall
} // namespace events
} // namespace osquery

View File

@ -1,113 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/system/linux/ebpf/program.h>
#include <boost/optional.hpp>
#include <bitset>
#include <type_traits>
#include <unordered_map>
namespace osquery {
namespace events {
namespace syscall {
enum class EventType : __s32 {
Unknown = 0,
KillEnter = 1,
KillExit = -KillEnter,
SetuidEnter = 2,
SetuidExit = -SetuidEnter,
};
static constexpr std::size_t kCommSize = 16u;
constexpr EventType flipEventType(EventType const type) noexcept {
return static_cast<EventType>(
-static_cast<std::underlying_type<EventType>::type>(type));
}
constexpr bool isEventTypeExit(EventType const type) noexcept {
return static_cast<std::underlying_type<EventType>::type>(type) < 0;
}
constexpr bool isEventTypeEnter(EventType const type) noexcept {
return 0 < static_cast<std::underlying_type<EventType>::type>(type);
}
struct Event {
// Common part for all events whether Enter or Exit
EventType type;
__s32 pid;
__s32 tgid;
// Body means different things for each Enter type.
// For all Exit types Body is always the same - just return value.
union Body {
struct KillEnter {
/* -44 type */
/* -40 pid */
/* -36 tgid */
/* -32 */ char comm[kCommSize];
/* -16 */ __s32 arg_pid;
/* -12 */ __s32 arg_sig;
/* -8 */ __u32 uid;
/* -4 */ __u32 gid;
} kill_enter;
struct SetuidEnter {
/* -40 type */
/* -36 pid */
/* -32 tgid */
/* -28 */ char comm[kCommSize];
/* -12 */ __s32 arg_uid;
/* -8 */ __u32 uid;
/* -4 */ __u32 gid;
} setuid_enter;
struct Exit {
/* -16 type */
/* -12 pid */
/* -8 tgid */
/* -4 */ __s32 ret;
} exit;
} body;
// This value is used by EnterExitJoiner, final return value of the syscall
// is placed here as a result of join().
// Also this member is used by EnterExitJoiner to preserve the age of the
// event.
__s32 return_value;
};
class EnterExitJoiner {
public:
boost::optional<Event> join(Event event);
bool isEmpty() const;
using CounterType = int;
static constexpr std::size_t KeyBitSize = 32u * 3u;
using KeyType = std::bitset<KeyBitSize>;
private:
void drop_stuck_events();
private:
CounterType counter_ = 0;
std::unordered_multimap<KeyType, Event> table_;
};
} // namespace syscall
} // namespace events
} // namespace osquery

View File

@ -1,231 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/experimental/tracing/linux/syscalls_programs.h>
#include <boost/core/demangle.hpp>
namespace osquery {
namespace events {
namespace {
bool constexpr kIsDebug =
#ifndef NDEBUG
true;
#else
false;
#endif
} // namespace
Expected<ebpf::Program, ebpf::Program::Error> genLinuxKillEnterProgram(
enum bpf_prog_type prog_type, PerfEventCpuMap const& cpu_map) {
constexpr int kKillEnterSize = 44;
static_assert(sizeof(syscall::EventType) + sizeof(syscall::Event::pid) +
sizeof(syscall::Event::tgid) +
sizeof(syscall::Event::Body::KillEnter) ==
kKillEnterSize,
"A program below relies on certain size of output struct");
// clang-format off
return ebpf::Program::load({
// code , dst reg , src reg , offset , immediate constant(k)
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_6, BPF_REG_1, 0, 0}, // r6 = r1
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_1, 0, 0, 0}, // r1 = 0
// this part of the stack is for comm string, let's initialize it with a '\0'
{BPF_STX | BPF_DW | BPF_MEM , BPF_REG_10, BPF_REG_1, -32, 0},
{BPF_STX | BPF_DW | BPF_MEM , BPF_REG_10, BPF_REG_1, -24, 0},
// put syscall code into final event struct, @see syscall::Event::type
{BPF_ST | BPF_W | BPF_MEM , BPF_REG_10, 0, -kKillEnterSize, static_cast<__s32>(syscall::EventType::KillEnter)}, // Event.type
// call fanction get_current_uid_gid()
{BPF_JMP | BPF_K | BPF_CALL , 0, 0, 0, BPF_FUNC_get_current_uid_gid},
// put first [0..32] bits of return value (R0) which is uid into final event struct, offset -8
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_0, -8, 0}, // Event.uid
{BPF_ALU64 | BPF_K | BPF_RSH , 0, 0, 0, 32},
// put first [32..65] bits of return value (R0) which is gid into final event struct, offset -4
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_0, -4, 0}, // Event.gid
// call fanction get_current_pid_tgid()
{BPF_JMP | BPF_K | BPF_CALL , 0, 0, 0, BPF_FUNC_get_current_pid_tgid},
// put first [0..32] bits of return value (R0) which is pid into final event struct, offset -40
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_0, -40, 0}, // Event.body.kill_enter.pid
{BPF_ALU64 | BPF_K | BPF_RSH , 0, 0, 0, 32},
// put first [0..32] bits of return value (R0) which is tgid into final event struct, offset -36
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_0, -36, 0}, // Event.body.kill_enter.tgid
// put stack pointer (register R10) to register R1, it's gonna be a first argument
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_1, BPF_REG_10, 0, 0}, // r1 = r10
// make R1 pointing to Event.body.kill_enter.comm in final event struct, offset -32
{BPF_ALU64 | BPF_K | BPF_ADD , BPF_REG_1, 0, 0, -32}, // r1 += -32
// put size of Event.body.kill_enter.comm to R2, it's gonna be a second argument
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_2, 0, 0, syscall::kCommSize},
// call get_current_comm(char *buf=R1, int size_of_buf=R2)
{BPF_JMP | BPF_K | BPF_CALL , 0, 0, 0, BPF_FUNC_get_current_comm}, // call
// let's read arguments of kill syscall, see /sys/kernel/debug/tracing/events/syscalls/sys_enter_kill/format
// TODO: It's not portable, because format can differ on different systems, but let me fix it later.
// load value from ctx + 16 to R7, accordint to format it is PID - first argument of kill()
{BPF_LDX | BPF_DW | BPF_MEM , BPF_REG_7, BPF_REG_6, 16, 0},
// stor PID from R7 to final event struct on stack
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_7, -16, 0},
// load value from ctx + 24 to R7, accordint to format it is SIG - second argument of kill()
{BPF_LDX | BPF_DW | BPF_MEM , BPF_REG_7, BPF_REG_6, 24, 0},
// stor SIG from R7 to final event struct on stack
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_7, -12, 0},
// let's send everything to user space via perf_event_open()
// event located on top of the stack, so store pointer to the top of the stack (R10) to R4
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_4, BPF_REG_10, 0, 0},
// we need pointer to the beginning of the event, so substruct size of the sending struct from R4
{BPF_ALU64 | BPF_K | BPF_ADD , BPF_REG_4, 0, 0, -kKillEnterSize}, // r4 += -kKillEnterSize
// store ctx to R1
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_1, BPF_REG_6, 0, 0},
// store map with perf_event_open() buffers per CPU to R2
{BPF_LD | BPF_DW | BPF_IMM , BPF_REG_2, BPF_PSEUDO_MAP_FD, 0, cpu_map.fd()},
{BPF_LD | BPF_W | BPF_IMM , 0, 0, 0, 0}, // imm is 32, but we loading 64, so this is yet another "smart" trick
// we don't know current CPU, but kernel can take care of it, put -1 in R2
{BPF_LD | BPF_DW | BPF_IMM , BPF_REG_3, 0, 0, -1}, // r2 = -1 -> CPU
{BPF_LD | BPF_W | BPF_IMM , 0, 0, 0, 0}, // imm is 32, but we loading 64, so this is yet another "smart" trick
// R5 should be a size of the event
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_5, 0, 0, kKillEnterSize}, // r5 = kKillEnterSize
// call perf_event_output(ctx=R1, map=R2, flags=R3, data=R4, size=R5)
{BPF_JMP | BPF_K | BPF_CALL , 0, 0, 0, BPF_FUNC_perf_event_output}, // call
// put 0 as a return value of the program
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_0, 0, 0, 0}, // r0 = 0
// let's get out of here
{BPF_JMP | BPF_K | BPF_EXIT , 0, 0, 0, 0}, // exit
}, BPF_PROG_TYPE_TRACEPOINT, kIsDebug);
// clang-format on
}
Expected<ebpf::Program, ebpf::Program::Error> genLinuxSetuidEnterProgram(
enum bpf_prog_type prog_type, PerfEventCpuMap const& cpu_map) {
constexpr int kSetuidEnterSize = 40;
static_assert(sizeof(syscall::EventType) + sizeof(syscall::Event::pid) +
sizeof(syscall::Event::tgid) +
sizeof(syscall::Event::Body::SetuidEnter) ==
kSetuidEnterSize,
"A program below relies on certain size of output struct");
static_assert(static_cast<__s32>(syscall::EventType::SetuidEnter) ==
-static_cast<__s32>(syscall::EventType::SetuidExit),
"Enter and Exit codes must be convertible to each other by "
"multiplying to -1");
// clang-format off
return ebpf::Program::load({
// code , dst reg , src reg , offset , immediate constant(k)
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_6, BPF_REG_1, 0, 0}, // r6 = r1
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_1, 0, 0, 0}, // r1 = 0
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_1, -28, 0},
{BPF_STX | BPF_DW | BPF_MEM , BPF_REG_10, BPF_REG_1, -24, 0},
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_1, -16, 0},
// Event.type = SyscallEvent::EventType::SetuidEnter
{BPF_ST | BPF_W | BPF_MEM , BPF_REG_10, 0, -kSetuidEnterSize, static_cast<__s32>(syscall::EventType::SetuidEnter)},
{BPF_JMP | BPF_K | BPF_CALL , 0, 0, 0, BPF_FUNC_get_current_uid_gid},
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_0, -8, 0}, // Event.uid
{BPF_ALU64 | BPF_K | BPF_RSH , 0, 0, 0, 32},
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_0, -4, 0}, // Event.gid
{BPF_JMP | BPF_K | BPF_CALL , 0, 0, 0, BPF_FUNC_get_current_pid_tgid},
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_0, -36, 0}, // Event.body.pid
{BPF_ALU64 | BPF_K | BPF_RSH , 0, 0, 0, 32},
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_0, -32, 0}, // Event.body.tgid
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_1, BPF_REG_10, 0, 0}, // r1 = r10
{BPF_ALU64 | BPF_K | BPF_ADD , BPF_REG_1, 0, 0, -28}, // r1 += -36
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_2, 0, 0, syscall::kCommSize}, // r2 = SyscallEvent::kCommSize
{BPF_JMP | BPF_K | BPF_CALL , 0, 0, 0, BPF_FUNC_get_current_comm}, // call
{BPF_LDX | BPF_DW | BPF_MEM , BPF_REG_7, BPF_REG_6, 16, 0}, // see format: arg uid
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_7, -12, 0}, // Event.body.arg_uid
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_4, BPF_REG_10, 0, 0}, // r4 = r10
{BPF_ALU64 | BPF_K | BPF_ADD , BPF_REG_4, 0, 0, -kSetuidEnterSize}, // r4 += -kSetuidEnterSize
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_1, BPF_REG_6, 0, 0}, // r1 = r6
{BPF_LD | BPF_DW | BPF_IMM , BPF_REG_2, BPF_PSEUDO_MAP_FD, 0, cpu_map.fd()},
{BPF_LD | BPF_W | BPF_IMM , 0, 0, 0, 0}, // imm is 32, but we loading 64, so this is yet another "smart" trick
{BPF_LD | BPF_DW | BPF_IMM , BPF_REG_3, 0, 0, -1}, // r2 = -1 -> CPU
{BPF_LD | BPF_W | BPF_IMM , 0, 0, 0, 0}, // imm is 32, but we loading 64, so this is yet another "smart" trick
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_5, 0, 0, kSetuidEnterSize}, // r5 = kSetuidEnterSize
{BPF_JMP | BPF_K | BPF_CALL , 0, 0, 0, BPF_FUNC_perf_event_output}, // call
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_0, 0, 0, 0}, // r0 = 0
{BPF_JMP | BPF_K | BPF_EXIT , 0, 0, 0, 0}, // exit
}, BPF_PROG_TYPE_TRACEPOINT, kIsDebug);
// clang-format on
}
Expected<ebpf::Program, ebpf::Program::Error> genLinuxExitProgram(
enum bpf_prog_type prog_type,
PerfEventCpuMap const& cpu_map,
syscall::EventType type) {
constexpr int kExitSize = 16;
static_assert(sizeof(syscall::EventType) + sizeof(syscall::Event::pid) +
sizeof(syscall::Event::tgid) +
sizeof(syscall::Event::Body::Exit) ==
kExitSize,
"A program below relies on certain size of output struct");
// clang-format off
return ebpf::Program::load({
// code , dst reg , src reg , offset , immediate constant(k)
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_6, BPF_REG_1, 0, 0}, // r6 = r1
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_1, 0, 0, 0}, // r1 = 0
{BPF_ST | BPF_W | BPF_MEM , BPF_REG_10, 0, -kExitSize, static_cast<__s32>(type)}, // type = syscall::EventType::Exit*
{BPF_JMP | BPF_K | BPF_CALL , 0, 0, 0, BPF_FUNC_get_current_pid_tgid},
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_0, -12, 0}, // Event.pid
{BPF_ALU64 | BPF_K | BPF_RSH , 0, 0, 0, 32}, // r0 >>= 32
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_0, -8, 0}, // Event.tgid
{BPF_LDX | BPF_DW | BPF_MEM , BPF_REG_7, BPF_REG_6, 16, 0}, // see format: ret
{BPF_STX | BPF_W | BPF_MEM , BPF_REG_10, BPF_REG_7, -4, 0}, // Event.body.return_value
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_4, BPF_REG_10, 0, 0}, // r4 = r10
{BPF_ALU64 | BPF_K | BPF_ADD , BPF_REG_4, 0, 0, -kExitSize}, // r4 += -kExitSize
{BPF_ALU64 | BPF_X | BPF_MOV , BPF_REG_1, BPF_REG_6, 0, 0}, // r1 = r6
{BPF_LD | BPF_DW | BPF_IMM , BPF_REG_2, BPF_PSEUDO_MAP_FD, 0, cpu_map.fd()},
{BPF_LD | BPF_W | BPF_IMM , 0, 0, 0, 0}, // imm is 32, but we loading 64, so this is yet another "smart" trick
{BPF_LD | BPF_DW | BPF_IMM , BPF_REG_3, 0, 0, -1}, // r2 = -1 -> CPU
{BPF_LD | BPF_W | BPF_IMM , 0, 0, 0, 0}, // imm is 32, but we loading 64, so this is yet another "smart" trick
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_5, 0, 0, kExitSize}, // r5 = kExitSize
{BPF_JMP | BPF_K | BPF_CALL , 0, 0, 0, BPF_FUNC_perf_event_output}, // call
{BPF_ALU64 | BPF_K | BPF_MOV , BPF_REG_0, 0, 0, 0}, // r0 = 0
{BPF_JMP | BPF_K | BPF_EXIT , 0, 0, 0, 0}, // exit
}, BPF_PROG_TYPE_TRACEPOINT, kIsDebug);
// clang-format on
}
Expected<ebpf::Program, ebpf::Program::Error> genLinuxProgram(
enum bpf_prog_type prog_type,
PerfEventCpuMap const& cpu_map,
syscall::EventType type) {
if (syscall::isEventTypeExit(type)) {
return genLinuxExitProgram(prog_type, cpu_map, type);
} else if (syscall::EventType::KillEnter == type) {
return genLinuxKillEnterProgram(prog_type, cpu_map);
} else if (syscall::EventType::SetuidEnter == type) {
return genLinuxSetuidEnterProgram(prog_type, cpu_map);
} else {
return createError(ebpf::Program::Error::Unknown)
<< "There is no program for type(" << static_cast<int>(type)
<< ") system call " << boost::core::demangle(typeid(type).name())
<< "(" << static_cast<int>(type) << ")";
}
}
} // namespace events
} // namespace osquery

View File

@ -1,29 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/experimental/tracing/linux/syscall_event.h>
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/system/linux/ebpf/map.h>
#include <osquery/utils/system/linux/ebpf/program.h>
namespace osquery {
namespace events {
using PerfEventCpuMap = ebpf::Map<int, int, BPF_MAP_TYPE_PERF_EVENT_ARRAY>;
Expected<ebpf::Program, ebpf::Program::Error> genLinuxProgram(
enum bpf_prog_type prog_type,
PerfEventCpuMap const& cpu_map,
syscall::EventType type);
} // namespace events
} // namespace osquery

View File

@ -1,34 +0,0 @@
# Copyright (c) 2014-present, The osquery authors
#
# This source code is licensed as defined by the LICENSE file found in the
# root directory of this source tree.
#
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
function(osqueryExperimentalTracingLinuxTestsMain)
if(DEFINED PLATFORM_LINUX)
generateOsqueryExperimentalTracingLinuxTestsEbpftracepointtestsTest()
endif()
endfunction()
function(generateOsqueryExperimentalTracingLinuxTestsEbpftracepointtestsTest)
set(source_files
empty.cpp
)
if(DEFINED PLATFORM_LINUX)
list(APPEND source_files syscall_event.cpp)
endif()
add_osquery_executable(osquery_experimental_tracing_linux_tests_ebpftracepointtests-test ${source_files})
target_link_libraries(osquery_experimental_tracing_linux_tests_ebpftracepointtests-test PRIVATE
osquery_cxx_settings
osquery_experimental_tracing_linux_probesevents
thirdparty_googletest
)
endfunction()
osqueryExperimentalTracingLinuxTestsMain()

View File

@ -1,8 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/

View File

@ -1,213 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <gtest/gtest.h>
#include <osquery/experimental/tracing/linux/syscall_event.h>
namespace osquery {
namespace {
class SyscallsTracepointTests : public testing::Test {};
class EnterExitJoinerTests : public testing::Test {};
template <events::syscall::EventType enter, events::syscall::EventType exit>
void checkEventPair() {
static_assert(enter == events::syscall::flipEventType(exit),
"flipEventType have to flip Exit to Enter");
static_assert(exit == events::syscall::flipEventType(enter),
"flipEventType have to flip Enter to Exit");
static_assert(enter == events::syscall::flipEventType(
events::syscall::flipEventType(enter)),
"flipEventType applied twice to Enter have to return exactly "
"the same Enter");
static_assert(exit == events::syscall::flipEventType(
events::syscall::flipEventType(exit)),
"flipEventType applied twice to Exit have to return exactly "
"the same Exit");
}
TEST_F(SyscallsTracepointTests, SyscallEvent_flipType) {
checkEventPair<events::syscall::EventType::KillEnter,
events::syscall::EventType::KillExit>();
checkEventPair<events::syscall::EventType::SetuidEnter,
events::syscall::EventType::SetuidExit>();
static_assert(
events::syscall::EventType::Unknown ==
events::syscall::flipEventType(events::syscall::EventType::Unknown),
"syscall::EventType::Unknown could not be fliped");
}
TEST_F(SyscallsTracepointTests, SyscallEvent_isTypeExit) {
static_assert(
events::syscall::isEventTypeExit(events::syscall::EventType::KillExit),
"");
static_assert(
events::syscall::isEventTypeExit(events::syscall::EventType::SetuidExit),
"");
static_assert(
!events::syscall::isEventTypeExit(events::syscall::EventType::Unknown),
"");
static_assert(!events::syscall::isEventTypeExit(
events::syscall::EventType::SetuidEnter),
"");
static_assert(!events::syscall::isEventTypeExit(
events::syscall::EventType::SetuidEnter),
"");
}
TEST_F(SyscallsTracepointTests, SyscallEvent_isTypeEnter) {
static_assert(
!events::syscall::isEventTypeEnter(events::syscall::EventType::KillExit),
"");
static_assert(!events::syscall::isEventTypeEnter(
events::syscall::EventType::SetuidExit),
"");
static_assert(
!events::syscall::isEventTypeEnter(events::syscall::EventType::Unknown),
"");
static_assert(events::syscall::isEventTypeEnter(
events::syscall::EventType::SetuidEnter),
"");
static_assert(events::syscall::isEventTypeEnter(
events::syscall::EventType::SetuidEnter),
"");
}
TEST_F(EnterExitJoinerTests,
EnterExitJoiner_many_pair_enter_exit_events_with_different_pid) {
auto joiner = events::syscall::EnterExitJoiner{};
{
auto enter_event = events::syscall::Event{};
enter_event.type = events::syscall::EventType::SetuidEnter;
enter_event.tgid = 146;
enter_event.body.setuid_enter.arg_uid = 48;
enter_event.body.setuid_enter.uid = 49;
enter_event.body.setuid_enter.gid = 50;
enter_event.return_value = -1;
for (int pid = 0; pid < 64; ++pid) {
enter_event.pid = pid;
auto out = joiner.join(enter_event);
ASSERT_FALSE(out);
}
}
auto exit_event = events::syscall::Event{};
exit_event.type = events::syscall::EventType::SetuidExit;
exit_event.tgid = 146;
exit_event.body.exit.ret = -59;
for (int pid = 0; pid < 64; ++pid) {
exit_event.pid = pid;
auto event = joiner.join(exit_event);
ASSERT_TRUE(event);
EXPECT_EQ(event->type, events::syscall::EventType::SetuidEnter);
EXPECT_EQ(event->pid, pid);
EXPECT_EQ(event->tgid, 146);
EXPECT_EQ(event->body.setuid_enter.arg_uid, 48);
EXPECT_EQ(event->body.setuid_enter.uid, 49);
EXPECT_EQ(event->body.setuid_enter.gid, 50);
EXPECT_EQ(event->return_value, -59);
}
EXPECT_TRUE(joiner.isEmpty());
}
TEST_F(EnterExitJoinerTests, EnterExitJoiner_one_non_paired_event_by_pid) {
auto joiner = events::syscall::EnterExitJoiner{};
auto enter_event = events::syscall::Event{};
enter_event.type = events::syscall::EventType::SetuidEnter;
enter_event.pid = 45;
enter_event.tgid = 146;
enter_event.body.setuid_enter.arg_uid = 48;
enter_event.body.setuid_enter.uid = 49;
enter_event.body.setuid_enter.gid = 50;
enter_event.return_value = -1;
auto out = joiner.join(enter_event);
ASSERT_FALSE(out);
auto exit_event = events::syscall::Event{};
exit_event.type = events::syscall::EventType::SetuidExit;
exit_event.pid = enter_event.pid + 12; // pid is different
exit_event.tgid = enter_event.tgid;
exit_event.body.exit.ret = -59;
auto event = joiner.join(exit_event);
ASSERT_FALSE(event);
ASSERT_FALSE(joiner.isEmpty());
}
TEST_F(EnterExitJoinerTests, EnterExitJoiner_one_non_paired_event_by_type) {
auto joiner = events::syscall::EnterExitJoiner{};
auto enter_event = events::syscall::Event{};
enter_event.type = events::syscall::EventType::SetuidEnter;
enter_event.pid = 45;
enter_event.tgid = 146;
enter_event.body.setuid_enter.arg_uid = 48;
enter_event.body.setuid_enter.uid = 49;
enter_event.body.setuid_enter.gid = 50;
enter_event.return_value = -1;
auto out = joiner.join(enter_event);
ASSERT_FALSE(out);
auto exit_event = events::syscall::Event{};
exit_event.type = events::syscall::EventType::KillExit; // type is different
exit_event.pid = enter_event.pid;
exit_event.tgid = enter_event.tgid;
exit_event.body.exit.ret = -59;
auto event = joiner.join(exit_event);
ASSERT_FALSE(event);
EXPECT_FALSE(joiner.isEmpty());
}
TEST_F(EnterExitJoinerTests, EnterExitJoiner_many_same_enter_exit_events) {
auto joiner = events::syscall::EnterExitJoiner{};
auto enter_event = events::syscall::Event{};
enter_event.type = events::syscall::EventType::SetuidEnter;
enter_event.pid = 218;
enter_event.tgid = 146;
enter_event.body.setuid_enter.arg_uid = 165;
enter_event.body.setuid_enter.uid = 49;
enter_event.body.setuid_enter.gid = 50;
enter_event.return_value = -1;
for (int i = 0; i < 12; ++i) {
joiner.join(enter_event);
}
auto exit_event = events::syscall::Event{};
exit_event.type = events::syscall::EventType::SetuidExit;
exit_event.pid = enter_event.pid;
exit_event.tgid = enter_event.tgid;
exit_event.body.exit.ret = -59;
for (int i = 0; i < 12; ++i) {
auto event = joiner.join(exit_event);
ASSERT_TRUE(event);
EXPECT_EQ(event->type, events::syscall::EventType::SetuidEnter);
EXPECT_EQ(event->pid, enter_event.pid);
EXPECT_EQ(event->tgid, enter_event.tgid);
EXPECT_EQ(event->body.setuid_enter.arg_uid,
enter_event.body.setuid_enter.arg_uid);
EXPECT_EQ(event->body.setuid_enter.uid, enter_event.body.setuid_enter.uid);
EXPECT_EQ(event->body.setuid_enter.gid, enter_event.body.setuid_enter.gid);
EXPECT_EQ(event->return_value, exit_event.body.exit.ret);
}
EXPECT_TRUE(joiner.isEmpty());
}
} // namespace
} // namespace osquery

View File

@ -1,36 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/experimental/tracing/syscalls_tracing.h>
#ifdef LINUX
#include <osquery/experimental/tracing/syscalls_tracing_impl.h>
#endif
#include <osquery/core/flags.h>
#include <osquery/logger/logger.h>
namespace osquery {
DEFINE_bool(enable_experimental_tracing,
false,
"Experimental syscalls tracing");
namespace events {
void init_syscall_tracing() {
#ifdef LINUX
if (FLAGS_enable_experimental_tracing) {
LOG(INFO) << "Experimental syscall tracing is enabled";
impl::runSyscallTracingService();
}
#endif
}
} // namespace events
} // namespace osquery

View File

@ -1,18 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
namespace osquery {
namespace events {
void init_syscall_tracing();
} // namespace events
} // namespace osquery

View File

@ -1,155 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/experimental/tracing/syscalls_tracing_impl.h>
#include <osquery/experimental/events_stream/events_stream.h>
#include <osquery/experimental/tracing/linux/probes.h>
#include <osquery/dispatcher/dispatcher.h>
#include <osquery/logger/logger.h>
#include <osquery/utils/caches/lru.h>
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/json/json.h>
#include <osquery/utils/system/linux/proc/proc.h>
#include <osquery/utils/system/time.h>
namespace osquery {
namespace events {
namespace {
class ProcCmdlineRetriever {
public:
// This is experimentally obtained number for the command line strings
// preserved in cache. If you need to decrease number of cache misses or to
// decrease memory consumption don't be shy to change it.
static constexpr auto kCacheCapacity = std::size_t{256};
explicit ProcCmdlineRetriever() : cache_(kCacheCapacity) {}
std::string const& get(pid_t proc_pid) {
if (auto cmd_ptr = cache_.get(proc_pid)) {
return *cmd_ptr;
} else {
return *cache_.insert(proc_pid, proc::cmdline(proc_pid));
}
}
private:
caches::LRU<pid_t, std::string> cache_;
};
enum class Error {
InitialisationProblem = 1,
RuntimeProblem = 2,
DeinitializationProblem = 3,
};
ExpectedSuccess<Error> runSyscallTracing() {
auto probes_exp = ::osquery::events::LinuxProbesControl::spawn();
if (probes_exp.isError()) {
return createError(Error::InitialisationProblem, probes_exp.takeError())
<< "linux probes control spawn failed";
}
auto probes = probes_exp.take();
auto kill_trace_on_exp = probes.traceKill();
if (kill_trace_on_exp.isError()) {
return createError(Error::InitialisationProblem,
kill_trace_on_exp.takeError())
<< "kill tracing initialisation failed";
}
auto setuid_trace_on_exp = probes.traceSetuid();
if (setuid_trace_on_exp.isError()) {
return createError(Error::InitialisationProblem,
setuid_trace_on_exp.takeError())
<< "setuid tracing initialisation failed";
}
auto output_batch = ebpf::PerfOutputsPoll<
::osquery::events::syscall::Event>::MessageBatchType{};
auto event_joiner = ::osquery::events::syscall::EnterExitJoiner{};
auto cmdline_getter = ProcCmdlineRetriever{};
while (true) {
auto status = probes.getReader().read(output_batch);
if (status.isError()) {
return createError(Error::RuntimeProblem, status.takeError())
<< "event read failed";
}
for (const auto& event : output_batch) {
auto final_event = event_joiner.join(event);
if (final_event) {
auto event_json = JSON{};
auto event_str = std::string{};
event_json.add("time", getUnixTime());
event_json.add("pid", final_event->pid);
event_json.add("tgid", final_event->tgid);
event_json.add("cmdline", cmdline_getter.get(final_event->pid));
event_json.add("return", final_event->return_value);
if (final_event->type ==
::osquery::events::syscall::EventType::KillEnter) {
event_json.add("type", "kill");
event_json.add("uid", final_event->body.kill_enter.uid);
event_json.add("gid", final_event->body.kill_enter.gid);
event_json.add("comm", final_event->body.kill_enter.comm);
event_json.add("arg_pid", final_event->body.kill_enter.arg_pid);
event_json.add(
"arg_cmdline",
cmdline_getter.get(final_event->body.kill_enter.arg_pid));
event_json.add("arg_sig", final_event->body.kill_enter.arg_sig);
} else if (final_event->type ==
::osquery::events::syscall::EventType::SetuidEnter) {
event_json.add("type", "setuid");
event_json.add("uid", final_event->body.setuid_enter.uid);
event_json.add("gid", final_event->body.setuid_enter.gid);
event_json.add("comm", final_event->body.setuid_enter.comm);
event_json.add("arg_uid", final_event->body.setuid_enter.arg_uid);
} else {
event_json.add("type", "unknown");
}
auto status_json_to_string = event_json.toString(event_str);
if (status_json_to_string.ok()) {
osquery::events::dispatchSerializedEvent(event_str);
} else {
LOG(ERROR) << "Event serialisation failed: "
<< status_json_to_string.what();
}
}
}
}
return Success{};
}
class SyscallTracingRannable : public ::osquery::InternalRunnable {
public:
explicit SyscallTracingRannable()
: ::osquery::InternalRunnable("SyscallTracingRannable") {}
void start() override {
auto ret = runSyscallTracing();
if (ret.isError()) {
LOG(ERROR) << "Experimental syscall tracing failed: "
<< ret.getError().getMessage();
}
}
void stop() override {}
};
} // namespace
namespace impl {
void runSyscallTracingService() {
Dispatcher::addService(std::make_shared<SyscallTracingRannable>());
}
} // namespace impl
} // namespace events
} // namespace osquery

View File

@ -1,21 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
namespace osquery {
namespace events {
namespace impl {
void runSyscallTracingService();
}
} // namespace events
} // namespace osquery

View File

@ -44,7 +44,6 @@ function(generateOsqueryMain)
osquery_dispatcher
osquery_dispatcher_scheduler
osquery_experimental_eventsstream_registry
osquery_experimental_tracing_syscallstracing
osquery_extensions
osquery_extensions_implthrift
osquery_logger_datalogger

View File

@ -30,7 +30,6 @@ function(osqueryMainHarnesses)
osquery_devtools
osquery_dispatcher
osquery_experimental_eventsstream_registry
osquery_experimental_tracing_syscallstracing
osquery_extensions
osquery_extensions_implthrift
osquery_logger_datalogger

View File

@ -32,8 +32,6 @@
#include <osquery/registry/registry_factory.h>
#include <osquery/sql/sqlite_util.h>
#include <osquery/experimental/tracing/syscalls_tracing.h>
namespace fs = boost::filesystem;
namespace osquery {
@ -108,8 +106,6 @@ void startDaemon(Initializer& runner) {
// Begin the schedule runloop.
startScheduler();
osquery::events::init_syscall_tracing();
runner.waitForShutdown();
}

View File

@ -5,7 +5,4 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
add_subdirectory("ebpf")
add_subdirectory("perf_event")
add_subdirectory("proc")
add_subdirectory("tracing")

View File

@ -1,57 +0,0 @@
# Copyright (c) 2014-present, The osquery authors
#
# This source code is licensed as defined by the LICENSE file found in the
# root directory of this source tree.
#
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
function(osqueryUtilsSystemLinuxEbpfMain)
if(OSQUERY_BUILD_TESTS)
add_subdirectory("tests")
endif()
generateOsqueryUtilsSystemLinuxEbpf()
endfunction()
function(generateOsqueryUtilsSystemLinuxEbpf)
if(DEFINED PLATFORM_LINUX)
set(source_files
ebpf.cpp
map.cpp
program.cpp
)
add_osquery_library(osquery_utils_system_linux_ebpf EXCLUDE_FROM_ALL ${source_files})
target_link_libraries(osquery_utils_system_linux_ebpf PUBLIC
osquery_cxx_settings
osquery_utils_conversions
osquery_utils_system_linux_perfevent
osquery_utils_system_cputopology
osquery_utils_expected
osquery_utils_versioning_semantic
osquery_utils_system_errno
osquery_logger
thirdparty_boost
thirdparty_googletest_headers
)
set(public_header_files
ebpf.h
map.h
perf_output.h
perf_output_impl.h
program.h
)
generateIncludeNamespace(osquery_utils_system_linux_ebpf "osquery/utils/system/linux/ebpf" "FILE_ONLY" ${public_header_files})
add_test(NAME osquery_utils_system_linux_ebpf_tests-test COMMAND osquery_utils_system_linux_ebpf_tests-test)
else()
add_osquery_library(osquery_utils_system_linux_ebpf INTERFACE)
endif()
endfunction()
osqueryUtilsSystemLinuxEbpfMain()

View File

@ -1,69 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/utils/system/linux/ebpf/ebpf.h>
#include <osquery/utils/versioning/semantic.h>
#include <boost/io/detail/quoted_manip.hpp>
#include <linux/version.h>
#include <sys/utsname.h>
#ifndef __NR_bpf
#if defined(__i386__)
#define __NR_bpf 357
#elif defined(__x86_64__)
#define __NR_bpf 321
#elif defined(__aarch64__)
#define __NR_bpf 280
#elif defined(__sparc__)
#define __NR_bpf 349
#elif defined(__s390__)
#define __NR_bpf 351
#else
#error __NR_bpf is undefined, probably this arch is not supported.
#endif
#endif // __NR_bpf
namespace osquery {
namespace ebpf {
constexpr int kMinimalLinuxVersionCode = KERNEL_VERSION(4, 9, 0);
Expected<bool, PosixError> isSupportedBySystem() {
struct utsname utsbuf;
if (uname(&utsbuf) == -1) {
return createError(to<PosixError>(errno))
<< "syscall uname() failed: " << boost::io::quoted(strerror(errno));
}
auto release_version_exp =
tryTo<SemanticVersion>(std::string(utsbuf.release));
if (release_version_exp.isError()) {
return createError(PosixError::Unknown, release_version_exp.takeError())
<< "uname() release field is malformed"
<< boost::io::quoted(utsbuf.release);
}
auto const version = release_version_exp.take();
return kMinimalLinuxVersionCode <=
KERNEL_VERSION(version.major, version.minor, version.patches);
}
Expected<int, PosixError> syscall(int cmd, union bpf_attr* attr) {
int const ret = ::syscall(__NR_bpf, cmd, attr, sizeof(union bpf_attr));
if (ret < 0) {
return createError(to<PosixError>(errno))
<< "bpf() syscall failed: " << boost::io::quoted(strerror(errno));
}
return ret;
}
} // namespace ebpf
} // namespace osquery

View File

@ -1,25 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/system/posix/errno.h>
#include <linux/bpf.h>
namespace osquery {
namespace ebpf {
Expected<bool, PosixError> isSupportedBySystem();
Expected<int, PosixError> syscall(int cmd, union bpf_attr* attr);
} // namespace ebpf
} // namespace osquery

View File

@ -1,99 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/utils/system/linux/ebpf/map.h>
#include <osquery/utils/system/linux/ebpf/ebpf.h>
#include <boost/io/detail/quoted_manip.hpp>
#include <cerrno>
#include <cstring>
namespace osquery {
namespace ebpf {
namespace impl {
Expected<int, MapError> mapCreate(enum bpf_map_type map_type,
std::size_t key_size,
std::size_t value_size,
std::size_t max_entries) {
union bpf_attr attr;
memset(&attr, 0, sizeof(union bpf_attr));
attr.map_type = map_type;
attr.key_size = static_cast<std::uint32_t>(key_size);
attr.value_size = static_cast<std::uint32_t>(value_size);
attr.max_entries = static_cast<std::uint32_t>(max_entries);
auto exp_bpf = syscall(BPF_MAP_CREATE, &attr);
if (exp_bpf.isError()) {
return createError(MapError::Unknown, exp_bpf.takeError())
<< "Creating eBPF map failed";
}
return exp_bpf.take();
}
ExpectedSuccess<MapError> mapUpdateElement(const int fd,
void const* key,
void const* value,
unsigned long long flags) {
union bpf_attr attr;
memset(&attr, 0, sizeof(union bpf_attr));
attr.map_fd = fd;
attr.key = reinterpret_cast<std::uint64_t>(key);
attr.value = reinterpret_cast<std::uint64_t>(value);
attr.flags = flags;
auto exp_bpf = syscall(BPF_MAP_UPDATE_ELEM, &attr);
if (exp_bpf.isError()) {
return createError(MapError::Unknown, exp_bpf.takeError())
<< "Updating value in eBPF map failed";
}
return Success{};
}
ExpectedSuccess<MapError> mapLookupElement(const int fd,
void const* key,
void* value) {
union bpf_attr attr;
memset(&attr, 0, sizeof(union bpf_attr));
attr.map_fd = fd;
attr.key = reinterpret_cast<std::uint64_t>(key);
attr.value = reinterpret_cast<std::uint64_t>(value);
auto exp_bpf = syscall(BPF_MAP_LOOKUP_ELEM, &attr);
if (exp_bpf.isError()) {
if (exp_bpf.getErrorCode() == PosixError::NOENT) {
return createError(MapError::NoSuchKey, exp_bpf.takeError())
<< "No such key in the eBPF map";
}
return createError(MapError::Unknown, exp_bpf.takeError())
<< "Look up in eBPF map failed";
}
return Success{};
}
ExpectedSuccess<MapError> mapDeleteElement(const int fd, void const* key) {
union bpf_attr attr;
memset(&attr, 0, sizeof(union bpf_attr));
attr.map_fd = fd;
attr.key = reinterpret_cast<std::uint64_t>(key);
auto exp_bpf = syscall(BPF_MAP_DELETE_ELEM, &attr);
if (exp_bpf.isError()) {
if (exp_bpf.getErrorCode() == PosixError::NOENT) {
return createError(MapError::NoSuchKey, exp_bpf.takeError())
<< "No such key in the eBPF map";
}
return createError(MapError::Unknown, exp_bpf.takeError())
<< "Deleting element from eBPF map failed";
}
return Success{};
}
} // namespace impl
} // namespace ebpf
} // namespace osquery

View File

@ -1,139 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/utils/expected/expected.h>
#include <linux/bpf.h>
namespace osquery {
namespace ebpf {
enum class MapError {
Unknown = 1,
NoSuchKey = 2,
};
namespace impl {
/**
* Do not use implementation functions directly, use Map class to create and
* manage eBPF map
*/
Expected<int, MapError> mapCreate(enum bpf_map_type map_type,
std::size_t key_size,
std::size_t value_size,
std::size_t max_entries);
ExpectedSuccess<MapError> mapUpdateElement(int fd,
void const* key,
void const* value,
unsigned long long flags);
ExpectedSuccess<MapError> mapLookupElement(int fd,
void const* key,
void* value);
ExpectedSuccess<MapError> mapDeleteElement(int fd, void const* key);
} // namespace impl
/**
* Proxy object for the eBPF map structure in kernel.
*/
template <typename KeyType, typename ValueType, enum bpf_map_type map_type>
class Map final {
private:
static_assert(
std::is_pod<KeyType>::value && std::is_pod<ValueType>::value,
"Both key type and value type must be a plain old data type (POD)");
/**
* The only constructor of Map is private for purpose. Use createMap function
* instead. Map should not be created in case of creating eBPF map failure.
*/
explicit Map(int fd, std::size_t size) : fd_(fd), size_(size) {}
public:
~Map() {
if (fd_ >= 0) {
close(fd_);
}
}
Map(Map const&) = delete;
Map(Map && from) : fd_(from.fd_), size_(from.size_) {
from.fd_ = -1;
}
Map& operator=(Map const&) = delete;
Map& operator=(Map&& from) {
if (fd_ >= 0) {
close(fd_);
fd_ = -1;
}
std::swap(fd_, from.fd_);
std::swap(size_, from.size_);
return *this;
}
Expected<ValueType, MapError> lookupElement(KeyType const& key) const {
auto value = ValueType{};
auto exp = impl::mapLookupElement(
fd_, static_cast<void const*>(&key), static_cast<void*>(&value));
if (exp.isError()) {
return exp.takeError();
}
return value;
}
ExpectedSuccess<MapError> updateElement(KeyType const& key,
ValueType const& value,
unsigned long long flags = BPF_ANY) {
return impl::mapUpdateElement(fd_,
static_cast<void const*>(&key),
static_cast<void const*>(&value),
flags);
}
ExpectedSuccess<MapError> deleteElement(KeyType const& key) {
return impl::mapDeleteElement(fd_, static_cast<void const*>(&key));
}
std::size_t size() const {
return size_;
}
int fd() const {
return fd_;
}
template <typename KType, typename VType, enum bpf_map_type type>
friend Expected<Map<KType, VType, type>, MapError> createMap(
std::size_t size);
private:
int fd_ = -1;
std::size_t size_;
};
template <typename KeyType, typename ValueType, enum bpf_map_type map_type>
static Expected<Map<KeyType, ValueType, map_type>, MapError> createMap(
std::size_t size) {
auto exp =
impl::mapCreate(map_type, sizeof(KeyType), sizeof(ValueType), size);
if (exp.isError()) {
return exp.takeError();
}
return Map<KeyType, ValueType, map_type>(exp.take(), size);
}
} // namespace ebpf
} // namespace osquery

View File

@ -1,121 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/system/linux/perf_event/perf_event.h>
#include <osquery/logger/logger.h>
#include <gtest/gtest_prod.h>
#include <chrono>
#include <type_traits>
#include <vector>
#include <poll.h>
namespace osquery {
namespace ebpf {
enum class PerfOutputError {
Unknown = 1,
SystemError = 2,
LogicError = 3,
};
template <typename MessageType>
class PerfOutput final {
public:
PerfOutput(PerfOutput&&);
PerfOutput& operator=(PerfOutput&&);
PerfOutput(PerfOutput const&) = delete;
PerfOutput& operator=(PerfOutput const&) = delete;
~PerfOutput();
static Expected<PerfOutput<MessageType>, PerfOutputError> load(
std::size_t cpu, std::size_t size);
int fd() const;
void const* data() const;
std::size_t size() const;
using MessageBatchType = std::vector<MessageType>;
#pragma pack(push, 1)
struct WrappedMessage {
struct perf_event_header header;
std::uint32_t size;
MessageType msg;
};
#pragma pack(pop)
ExpectedSuccess<PerfOutputError> read(MessageBatchType& dst);
private:
explicit PerfOutput() = default;
ExpectedSuccess<PerfOutputError> unload();
void forceUnload();
static_assert(
sizeof(WrappedMessage) <
std::numeric_limits<
decltype(std::declval<struct perf_event_header>().size)>::max(),
"A MessageType is too big, linux perf event can not support it");
FRIEND_TEST(PerfOutputTests, move_constructor);
FRIEND_TEST(PerfOutputTests, assigning_constructor);
private:
std::size_t size_;
int fd_ = -1;
void* data_ptr_ = nullptr;
};
template <typename MessageType>
class PerfOutputsPoll final {
public:
explicit PerfOutputsPoll() = default;
PerfOutputsPoll(PerfOutputsPoll&&);
PerfOutputsPoll& operator=(PerfOutputsPoll&&);
PerfOutputsPoll(PerfOutputsPoll const&) = delete;
PerfOutputsPoll& operator=(PerfOutputsPoll const&) = delete;
std::size_t size() const;
/**
* Add new perf output to the poll
*/
ExpectedSuccess<PerfOutputError> add(PerfOutput<MessageType> output);
using MessageBatchType = typename PerfOutput<MessageType>::MessageBatchType;
/**
* Blocking method of reading new messages from the polling outputs
*/
ExpectedSuccess<PerfOutputError> read(MessageBatchType& batch);
private:
std::vector<PerfOutput<MessageType>> outputs_;
std::vector<struct pollfd> fds_;
const std::chrono::milliseconds poll_timeout_ = std::chrono::seconds{2};
};
} // namespace ebpf
} // namespace osquery
#include <osquery/utils/system/linux/ebpf/perf_output_impl.h>

View File

@ -1,315 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/utils/system/linux/cpu.h>
#include <osquery/utils/system/linux/perf_event/perf_event.h>
#include <osquery/logger/logger.h>
#include <boost/io/detail/quoted_manip.hpp>
#include <linux/hw_breakpoint.h>
#include <linux/perf_event.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <utility>
namespace osquery {
namespace ebpf {
template <typename MessageType>
PerfOutput<MessageType>::~PerfOutput() {
forceUnload();
}
template <typename MessageType>
PerfOutput<MessageType>::PerfOutput(PerfOutput&& other)
: size_(other.size_), fd_(other.fd_), data_ptr_(other.data_ptr_) {
other.fd_ = -1;
other.data_ptr_ = nullptr;
}
template <typename MessageType>
PerfOutput<MessageType>& PerfOutput<MessageType>::operator=(
PerfOutput&& other) {
std::swap(size_, other.size_);
std::swap(fd_, other.fd_);
std::swap(data_ptr_, other.data_ptr_);
return *this;
}
template <typename MessageType>
Expected<PerfOutput<MessageType>, PerfOutputError>
PerfOutput<MessageType>::load(std::size_t const cpu, std::size_t const size) {
auto instance = PerfOutput<MessageType>{};
instance.size_ = size;
struct perf_event_attr attr;
memset(&attr, 0, sizeof(struct perf_event_attr));
attr.type = PERF_TYPE_SOFTWARE;
attr.size = sizeof(struct perf_event_attr);
attr.config = PERF_COUNT_SW_BPF_OUTPUT;
attr.sample_period = 1;
attr.sample_type = PERF_SAMPLE_RAW;
attr.wakeup_events = 1;
attr.disabled = 1;
pid_t const pid = -1;
int const group_fd = -1;
unsigned long const flags = 0;
auto exp_fd = perf_event_open::syscall(
&attr, pid, static_cast<int>(cpu), group_fd, flags);
if (exp_fd.isError()) {
return createError(PerfOutputError::SystemError, exp_fd.takeError())
<< "Fail to create perf_event output point";
}
instance.fd_ = exp_fd.take();
instance.data_ptr_ = mmap(NULL,
instance.size_,
PROT_READ | PROT_WRITE,
MAP_SHARED,
instance.fd_,
0);
if (instance.data_ptr_ == MAP_FAILED) {
instance.data_ptr_ = nullptr;
return createError(PerfOutputError::SystemError)
<< "Fail to mmap memory for perf event of PerfOutput "
<< boost::io::quoted(strerror(errno));
}
if (ioctl(instance.fd_, PERF_EVENT_IOC_ENABLE, 0) < 0) {
return createError(PerfOutputError::SystemError)
<< "Fail to enable perf event of PerfOutput "
<< boost::io::quoted(strerror(errno));
}
return Expected<PerfOutput<MessageType>, PerfOutputError>(
std::move(instance));
}
template <typename MessageType>
ExpectedSuccess<PerfOutputError> PerfOutput<MessageType>::unload() {
if (fd_ < 0) {
return Success{};
}
bool failed = false;
std::string err_msg;
int ret = ioctl(fd_, PERF_EVENT_IOC_DISABLE, 0);
if (ret < 0) {
failed = true;
err_msg += " perf event disabling failed: \"";
err_msg += strerror(errno);
err_msg += "\". ";
}
if (data_ptr_ != nullptr) {
ret = munmap(data_ptr_, size_);
if (ret < 0) {
failed = true;
err_msg += " Memory unmap failed: \"";
err_msg += strerror(errno);
err_msg += "\".";
}
data_ptr_ = nullptr;
}
ret = close(fd_);
if (ret < 0) {
failed = true;
err_msg += " File descriptor was closed with error: \"";
err_msg += strerror(errno);
err_msg += "\".";
}
fd_ = -1;
if (failed) {
return createError(PerfOutputError::SystemError) << err_msg;
}
return Success{};
}
template <typename MessageType>
void PerfOutput<MessageType>::forceUnload() {
auto const exp = unload();
if (exp.isError()) {
LOG(ERROR) << "Could not unload perf event output point "
<< boost::io::quoted(exp.getError().getMessage());
}
}
template <typename MessageType>
int PerfOutput<MessageType>::fd() const {
return fd_;
}
template <typename MessageType>
void const* PerfOutput<MessageType>::data() const {
return data_ptr_;
}
template <typename MessageType>
std::size_t PerfOutput<MessageType>::size() const {
return size_;
}
namespace impl {
using ByteType = std::uint8_t;
/**
* Circular buffer reading is in separate function only for the tests
*
* |<--------------- mapped memory ------------------------------------------->|
* [header] |<---------------------- data_size ----------------------------->|
* [xxxx.....................xxxxxxxxxxxxxxxxxxxxxxxxxxx.......................]
* | | ^ |
* data_ptr data_tail | data_head
* wrapped messages
*/
template <typename WrappedMessage,
typename MessageType = decltype(std::declval<WrappedMessage>().msg)>
inline ExpectedSuccess<PerfOutputError>
consumeWrappedMessagesFromCircularBuffer(ByteType const* data_ptr,
__u64 data_tail,
__u64 const data_head,
__u64 const buffer_size,
std::vector<MessageType>& messages) {
__u64 offset = data_tail % buffer_size;
while (data_tail < data_head) {
if (offset + sizeof(WrappedMessage) > buffer_size) {
// wrapped_message is probably splited, let's do continuous copy
auto wrapped_message = WrappedMessage{};
__u64 record_offset = buffer_size - offset;
std::copy(data_ptr + offset,
data_ptr + buffer_size,
reinterpret_cast<ByteType*>(&wrapped_message));
offset = 0u;
auto const header_size = sizeof(wrapped_message.header);
if (record_offset < header_size) {
__u64 const header_remain_len = header_size - record_offset;
std::copy(
data_ptr,
data_ptr + header_remain_len,
reinterpret_cast<ByteType*>(&wrapped_message) + record_offset);
record_offset += header_remain_len;
offset += header_remain_len;
}
if (wrapped_message.header.size > record_offset) {
__u64 remain_len = wrapped_message.header.size - record_offset;
std::copy(
data_ptr + offset,
data_ptr + offset + remain_len,
reinterpret_cast<ByteType*>(&wrapped_message) + record_offset);
}
messages.push_back(wrapped_message.msg);
data_tail += wrapped_message.header.size;
offset = data_tail % buffer_size;
} else {
WrappedMessage wrapped_message;
memcpy(&wrapped_message, (data_ptr + offset), sizeof(WrappedMessage));
messages.emplace_back(wrapped_message.msg);
offset += wrapped_message.header.size;
data_tail += wrapped_message.header.size;
}
}
return Success{};
}
} // namespace impl
template <typename MessageType>
ExpectedSuccess<PerfOutputError> PerfOutput<MessageType>::read(
MessageBatchType& dst) {
static_assert(std::is_trivial<MessageType>::value,
"message type must be trivial, because it comes from ASM code "
"at the end");
if (fd_ < 0) {
return createError(PerfOutputError::LogicError)
<< "Attept to read from not loaded perf output";
}
auto header = static_cast<struct perf_event_mmap_page*>(data_ptr_);
if (header->data_head == header->data_tail) {
return Success{};
}
auto status = impl::consumeWrappedMessagesFromCircularBuffer<WrappedMessage>(
(impl::ByteType const*)data() + header->data_offset,
header->data_tail,
header->data_head,
header->data_size,
dst);
header->data_tail = header->data_head;
return status;
}
template <typename MessageType>
PerfOutputsPoll<MessageType>::PerfOutputsPoll(PerfOutputsPoll&& other)
: outputs_(std::move(other.outputs_)), fds_(std::move(other.fds_)) {
other.outputs_.clear();
other.fds_.clear();
}
template <typename MessageType>
PerfOutputsPoll<MessageType>& PerfOutputsPoll<MessageType>::operator=(
PerfOutputsPoll&& other) {
std::swap(outputs_, other.outputs_);
std::swap(fds_, other.fds_);
return *this;
}
template <typename MessageType>
std::size_t PerfOutputsPoll<MessageType>::size() const {
return outputs_.size();
}
template <typename MessageType>
ExpectedSuccess<PerfOutputError> PerfOutputsPoll<MessageType>::add(
PerfOutput<MessageType> output) {
if (outputs_.size() == cpu::kMaskSize) {
return createError(PerfOutputError::LogicError)
<< "osquery support no more than " << cpu::kMaskSize
<< " cpu, change cpu::kMaskSize and recompile";
}
struct pollfd pfd;
pfd.fd = output.fd();
pfd.events = POLLIN;
fds_.push_back(pfd);
outputs_.push_back(std::move(output));
return Success{};
}
template <typename MessageType>
ExpectedSuccess<PerfOutputError> PerfOutputsPoll<MessageType>::read(
PerfOutputsPoll<MessageType>::MessageBatchType& batch) {
while (true) {
int ret = ::poll(fds_.data(), fds_.size(), poll_timeout_.count());
if (ret < 0) {
return createError(PerfOutputError::SystemError)
<< "perf output polling failed" << strerror(errno);
} else if (ret == 0) {
// timeout; no event detected
} else {
batch.clear();
for (std::size_t index = 0; index < fds_.size(); ++index) {
if ((fds_[index].revents & POLLIN) != 0) {
fds_[index].revents = 0;
auto status = outputs_[index].read(batch);
if (status.isError()) {
return createError(PerfOutputError::LogicError, status.takeError())
<< "read in perf output poll failed";
}
}
}
return Success{};
}
}
}
} // namespace ebpf
} // namespace osquery

View File

@ -1,76 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/utils/system/linux/ebpf/program.h>
#include <osquery/utils/system/linux/ebpf/ebpf.h>
#include <boost/io/detail/quoted_manip.hpp>
#include <linux/version.h>
namespace osquery {
namespace ebpf {
Program::Program(Program&& other) : fd_(other.fd_) {
other.fd_ = -1;
}
Program& Program::operator=(Program&& other) {
std::swap(fd_, other.fd_);
return *this;
}
Program::~Program() {
if (fd_ > 0) {
close(fd_);
fd_ = -1;
}
}
Expected<Program, Program::Error> Program::load(
std::vector<struct bpf_insn> const prog,
enum bpf_prog_type const program_type,
bool const debug) {
static char const* kLicense = "GPL";
constexpr auto kLogBufSize = std::uint32_t{1 << 16};
auto bpf_log_buf = std::array<char, kLogBufSize>{};
union bpf_attr attr = {
.prog_type = program_type,
.insns = reinterpret_cast<__aligned_u64>(prog.data()),
.insn_cnt = static_cast<std::uint32_t>(prog.size()),
.license = reinterpret_cast<__aligned_u64>(kLicense),
.log_buf = 0u,
.log_size = 0u,
.log_level = 0u,
.kern_version = LINUX_VERSION_CODE,
};
if (debug) {
bpf_log_buf.fill('\0');
attr.log_buf = reinterpret_cast<std::uint64_t>(bpf_log_buf.data());
attr.log_size = reinterpret_cast<std::uint32_t>(kLogBufSize);
attr.log_level = 1;
}
auto instance = Program{};
auto fd_exp = syscall(BPF_PROG_LOAD, &attr);
if (fd_exp.isError()) {
return createError(Program::Error::Unknown, fd_exp.takeError())
<< "eBPF program load failed, bpf log: "
<< boost::io::quoted(bpf_log_buf.data());
}
instance.fd_ = fd_exp.take();
return Expected<Program, Program::Error>(std::move(instance));
}
int Program::fd() const {
return fd_;
}
} // namespace ebpf
} // namespace osquery

View File

@ -1,54 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/utils/expected/expected.h>
#include <linux/bpf.h>
#include <linux/version.h>
#if defined(LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 263946
#define BPF_PROG_TYPE_TRACEPOINT (bpf_prog_type)(BPF_PROG_TYPE_SCHED_ACT + 1)
#endif
namespace osquery {
namespace ebpf {
class Program final {
public:
Program(Program&& other);
Program& operator=(Program&& other);
Program(Program const&) = delete;
Program& operator=(Program const&) = delete;
~Program();
enum class Error {
Unknown = 1,
NotSupportedBySystem = 2,
};
static Expected<Program, Program::Error> load(
std::vector<struct bpf_insn> prog,
enum bpf_prog_type const program_type = BPF_PROG_TYPE_KPROBE,
bool const debug = false);
int fd() const;
private:
Program() = default;
private:
int fd_ = -1;
};
} // namespace ebpf
} // namespace osquery

View File

@ -1,39 +0,0 @@
# Copyright (c) 2014-present, The osquery authors
#
# This source code is licensed as defined by the LICENSE file found in the
# root directory of this source tree.
#
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
function(osqueryUtilsSystemLinuxEbpfTestsMain)
if(DEFINED PLATFORM_LINUX)
generateOsqueryUtilsSystemLinuxEbpfTestsTest()
endif()
endfunction()
function(generateOsqueryUtilsSystemLinuxEbpfTestsTest)
set(source_files
empty.cpp
)
if(DEFINED PLATFORM_LINUX)
set(platform_source_files
ebpf.cpp
map.cpp
perf_output.cpp
program.cpp
)
list(APPEND source_files ${platform_source_files})
endif()
add_osquery_executable(osquery_utils_system_linux_ebpf_tests-test ${source_files})
target_link_libraries(osquery_utils_system_linux_ebpf_tests-test PRIVATE
osquery_cxx_settings
osquery_utils_system_linux_ebpf
thirdparty_googletest
)
endfunction()
osqueryUtilsSystemLinuxEbpfTestsMain()

View File

@ -1,48 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <gtest/gtest.h>
#include <osquery/utils/system/linux/ebpf/ebpf.h>
namespace osquery {
namespace {
class EbpfTests : public testing::Test {};
TEST_F(EbpfTests, isSupportedBySystem) {
auto const exp = ebpf::isSupportedBySystem();
ASSERT_TRUE(exp.isValue()) << exp.getError().getMessage();
}
TEST_F(EbpfTests, sysEbpf_null_attr) {
auto const exp = ebpf::syscall(BPF_MAP_CREATE, nullptr);
ASSERT_TRUE(exp.isError());
ASSERT_EQ(exp.getErrorCode(), PosixError::FAULT);
}
TEST_F(EbpfTests, sysEbpf_create_map) {
auto const is_supported_exp = ebpf::isSupportedBySystem();
EXPECT_TRUE(is_supported_exp.isValue())
<< is_supported_exp.getError().getMessage();
if (is_supported_exp.get()) {
union bpf_attr attr;
memset(&attr, 0, sizeof(union bpf_attr));
attr.map_type = BPF_MAP_TYPE_ARRAY;
attr.key_size = 4;
attr.value_size = 4;
attr.max_entries = 12;
auto exp_bpf = ebpf::syscall(BPF_MAP_CREATE, &attr);
ASSERT_TRUE(exp_bpf.isValue());
ASSERT_GE(exp_bpf.get(), 0);
}
}
} // namespace
} // namespace osquery

View File

@ -1,8 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/

View File

@ -1,106 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/utils/system/linux/ebpf/map.h>
#include <osquery/utils/system/linux/ebpf/ebpf.h>
#include <osquery/logger/logger.h>
#include <gtest/gtest.h>
namespace osquery {
namespace {
class EbpfMapTests : public testing::Test {};
TEST_F(EbpfMapTests, int_key_int_value) {
if (!ebpf::isSupportedBySystem()) {
LOG(WARNING) << "This system does not support eBPF of required vesion, "
"test will be skipped";
return;
}
auto const size = std::size_t{12};
auto map_exp = ebpf::createMap<int, int, BPF_MAP_TYPE_HASH>(size);
ASSERT_TRUE(map_exp.isValue()) << map_exp.getError().getMessage();
auto map = map_exp.take();
ASSERT_EQ(map.size(), size);
{
auto exp = map.lookupElement(0);
ASSERT_TRUE(exp.isError());
EXPECT_EQ(exp.getError().getErrorCode(), ebpf::MapError::NoSuchKey);
}
{
auto exp = map.lookupElement(215);
ASSERT_TRUE(exp.isError());
EXPECT_EQ(exp.getError().getErrorCode(), ebpf::MapError::NoSuchKey);
}
{
auto exp = map.updateElement(5, 53);
ASSERT_TRUE(exp.isValue()) << exp.getError().getMessage();
}
{
auto exp = map.lookupElement(5);
ASSERT_TRUE(exp.isValue()) << exp.getError().getMessage();
ASSERT_EQ(exp.get(), 53);
}
{
// key could be greater a size, because it is a hash map
auto exp = map.updateElement(207, 8042);
ASSERT_TRUE(exp.isValue()) << exp.getError().getMessage();
}
{
auto exp = map.lookupElement(207);
ASSERT_TRUE(exp.isValue()) << exp.getError().getMessage();
ASSERT_EQ(exp.get(), 8042);
}
{
// let's try to delete some existing key
auto exp = map.deleteElement(207);
ASSERT_TRUE(exp.isValue()) << exp.getError().getMessage();
}
{
auto exp = map.lookupElement(207);
ASSERT_TRUE(exp.isError());
EXPECT_EQ(exp.getError().getErrorCode(), ebpf::MapError::NoSuchKey);
}
}
TEST_F(EbpfMapTests, int_key_struct_value) {
if (!ebpf::isSupportedBySystem()) {
LOG(WARNING) << "This system does not support eBPF of required vesion, "
"test will be skipped";
return;
}
struct Value {
int left;
int right;
};
auto const size = std::size_t{128};
auto map_exp = ebpf::createMap<int, Value, BPF_MAP_TYPE_ARRAY>(size);
ASSERT_TRUE(map_exp.isValue());
auto map = map_exp.take();
ASSERT_EQ(map.size(), size);
{
auto const v = Value{
.left = -9287,
.right = 2781,
};
auto exp = map.updateElement(72, v);
ASSERT_TRUE(exp.isValue()) << exp.getError().getMessage();
}
{
auto exp = map.lookupElement(72);
ASSERT_TRUE(exp.isValue()) << exp.getError().getMessage();
EXPECT_EQ(exp.get().left, -9287);
EXPECT_EQ(exp.get().right, 2781);
}
}
} // namespace
} // namespace osquery

View File

@ -1,211 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/utils/system/linux/ebpf/perf_output.h>
#include <gtest/gtest.h>
#include <array>
namespace osquery {
namespace ebpf {
class PerfOutputTests : public testing::Test {};
struct TestMessage {
int a_;
int b_;
char c_;
char d_;
};
TEST_F(PerfOutputTests, load) {
auto output_exp = ebpf::PerfOutput<TestMessage>::load(0u, 512u);
// permission denied, test runs under non root user
ASSERT_TRUE(output_exp.isError());
}
TEST_F(PerfOutputTests, move_constructor) {
auto buf = std::array<char, 4>{};
auto from_obj = ebpf::PerfOutput<TestMessage>{};
from_obj.size_ = buf.size();
from_obj.fd_ = 42;
from_obj.data_ptr_ = static_cast<void*>(buf.data());
auto to_obj = std::move(from_obj);
EXPECT_EQ(to_obj.size_, buf.size());
EXPECT_EQ(to_obj.fd_, 42);
EXPECT_EQ(to_obj.data_ptr_, static_cast<void*>(buf.data()));
EXPECT_LE(from_obj.fd_, 0);
EXPECT_EQ(from_obj.data_ptr_, nullptr);
// to prevent closing non existing file descriptor
to_obj.fd_ = -1;
}
TEST_F(PerfOutputTests, assigning_constructor) {
auto buf = std::array<char, 8>{};
auto from_obj = ebpf::PerfOutput<TestMessage>{};
from_obj.size_ = buf.size();
from_obj.fd_ = 42;
from_obj.data_ptr_ = static_cast<void*>(buf.data());
auto to_obj = ebpf::PerfOutput<TestMessage>{};
to_obj = std::move(from_obj);
EXPECT_EQ(to_obj.size_, buf.size());
EXPECT_EQ(to_obj.fd_, 42);
EXPECT_EQ(to_obj.data_ptr_, static_cast<void*>(buf.data()));
EXPECT_LE(from_obj.fd_, 0);
EXPECT_EQ(from_obj.data_ptr_, nullptr);
// to prevent closing non existing file descriptor
to_obj.fd_ = -1;
}
TEST_F(PerfOutputTests,
impl_consumeWrappedMessagesFromCircularBuffer_without_wrapping) {
using WrappedMessage = ebpf::PerfOutput<TestMessage>::WrappedMessage;
auto const test_size = std::size_t{9};
auto buf = std::vector<ebpf::impl::ByteType>(
sizeof(WrappedMessage) * test_size + 128, 0);
auto buf_ptr = &buf[0];
for (std::size_t i = 0; i < test_size; ++i) {
auto wrapped = WrappedMessage{};
wrapped.msg.a_ = i + 1;
wrapped.msg.b_ = i * 2 + 2;
wrapped.msg.c_ = 'j';
wrapped.msg.d_ = 'k';
wrapped.size = sizeof(TestMessage);
wrapped.header.type = PERF_RECORD_SAMPLE;
wrapped.header.size = sizeof(WrappedMessage);
auto const wrapped_ptr =
reinterpret_cast<ebpf::impl::ByteType const*>(&wrapped);
std::copy(wrapped_ptr, wrapped_ptr + sizeof(WrappedMessage), buf_ptr);
buf_ptr += sizeof(WrappedMessage);
}
auto dst = std::vector<TestMessage>{};
auto status =
ebpf::impl::consumeWrappedMessagesFromCircularBuffer<WrappedMessage>(
&buf[0],
0,
static_cast<std::size_t>(buf_ptr - &buf[0]),
buf.size(),
dst);
ASSERT_FALSE(status.isError());
ASSERT_EQ(dst.size(), test_size);
for (std::size_t i = 0; i < test_size; ++i) {
EXPECT_EQ(dst[i].a_, static_cast<int>(i + 1));
EXPECT_EQ(dst[i].b_, static_cast<int>(i * 2 + 2));
EXPECT_EQ(dst[i].c_, 'j');
EXPECT_EQ(dst[i].d_, 'k');
}
}
TEST_F(PerfOutputTests,
impl_consumeWrappedMessagesFromCircularBuffer_simply_wrapped) {
using WrappedMessage = ebpf::PerfOutput<TestMessage>::WrappedMessage;
auto const test_size = std::size_t{9};
auto buf =
std::vector<ebpf::impl::ByteType>(sizeof(WrappedMessage) * test_size, 0);
auto buf_ptr = &buf[0];
for (std::size_t i = 0; i < test_size; ++i) {
auto wrapped = WrappedMessage{};
wrapped.msg.a_ = i + 1;
wrapped.msg.b_ = i * 2 + 2;
wrapped.msg.c_ = 'y';
wrapped.msg.d_ = 'x';
wrapped.size = sizeof(TestMessage);
wrapped.header.type = PERF_RECORD_SAMPLE;
wrapped.header.size = sizeof(WrappedMessage);
auto const wrapped_ptr =
reinterpret_cast<ebpf::impl::ByteType const*>(&wrapped);
std::copy(wrapped_ptr, wrapped_ptr + sizeof(WrappedMessage), buf_ptr);
buf_ptr += sizeof(WrappedMessage);
}
auto dst = std::vector<TestMessage>{};
auto status =
ebpf::impl::consumeWrappedMessagesFromCircularBuffer<WrappedMessage>(
&buf[0],
sizeof(WrappedMessage),
buf.size() + sizeof(WrappedMessage),
buf.size(),
dst);
ASSERT_FALSE(status.isError()) << status.getError().getMessage();
ASSERT_EQ(dst.size(), test_size);
for (std::size_t i = 0; i < test_size; ++i) {
EXPECT_EQ(dst[i].c_, 'y') << i;
EXPECT_EQ(dst[i].d_, 'x');
}
EXPECT_EQ(dst.back().a_, 1);
EXPECT_EQ(dst.back().b_, 2);
EXPECT_EQ(dst[0].a_, 2);
EXPECT_EQ(dst[0].b_, 4);
}
TEST_F(PerfOutputTests,
impl_consumeWrappedMessagesFromCircularBuffer_splited_record_wrapping) {
using WrappedMessage = ebpf::PerfOutput<TestMessage>::WrappedMessage;
auto const test_size = std::size_t{3};
auto buf = std::vector<ebpf::impl::ByteType>(
sizeof(WrappedMessage) * test_size + 154, 0);
auto const first_part_size = 8;
auto const tail = buf.size() - sizeof(WrappedMessage) - first_part_size;
auto const head = tail + sizeof(WrappedMessage) * test_size;
auto wrapped = WrappedMessage{};
wrapped.msg.a_ = 1;
wrapped.msg.b_ = 2;
wrapped.msg.c_ = 't';
wrapped.msg.d_ = 'i';
wrapped.size = sizeof(TestMessage);
wrapped.header.type = PERF_RECORD_SAMPLE;
wrapped.header.size = sizeof(WrappedMessage);
auto const wrapped_ptr =
reinterpret_cast<ebpf::impl::ByteType const*>(&wrapped);
std::copy(wrapped_ptr, wrapped_ptr + sizeof(WrappedMessage), &buf[0] + tail);
wrapped.msg.a_ = 3;
wrapped.msg.b_ = 4;
std::copy(wrapped_ptr,
wrapped_ptr + first_part_size,
&buf[0] + tail + sizeof(WrappedMessage));
std::copy(wrapped_ptr + first_part_size,
wrapped_ptr + sizeof(WrappedMessage),
&buf[0]);
wrapped.msg.a_ = 5;
wrapped.msg.b_ = 6;
std::copy(wrapped_ptr,
wrapped_ptr + sizeof(WrappedMessage),
&buf[0] + sizeof(WrappedMessage) - first_part_size);
auto dst = std::vector<TestMessage>{};
auto status =
ebpf::impl::consumeWrappedMessagesFromCircularBuffer<WrappedMessage>(
&buf[0], tail, head, buf.size(), dst);
ASSERT_FALSE(status.isError()) << status.getError().getMessage();
ASSERT_EQ(dst.size(), test_size);
for (std::size_t i = 0; i < test_size; ++i) {
EXPECT_EQ(dst[i].c_, 't');
EXPECT_EQ(dst[i].d_, 'i');
}
EXPECT_EQ(dst[0].a_, 1);
EXPECT_EQ(dst[0].b_, 2);
EXPECT_EQ(dst[1].a_, 3);
EXPECT_EQ(dst[1].b_, 4);
EXPECT_EQ(dst[2].a_, 5);
EXPECT_EQ(dst[2].b_, 6);
}
} // namespace ebpf
} // namespace osquery

View File

@ -1,43 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/utils/system/linux/ebpf/program.h>
#include <osquery/utils/system/linux/ebpf/ebpf.h>
#include <osquery/logger/logger.h>
#include <gtest/gtest.h>
namespace osquery {
namespace {
class EbpfProgramTests : public testing::Test {};
bool const kDebug =
#ifndef NDEBUG
true;
#else
false;
#endif
TEST_F(EbpfProgramTests, empty_debug) {
auto const ebpf_exp = ebpf::isSupportedBySystem();
EXPECT_TRUE(ebpf_exp.isValue()) << ebpf_exp.getError().getMessage();
if (!ebpf_exp.get()) {
LOG(WARNING) << "This system does not support eBPF of required vesion, "
"test will be skipped";
return;
}
auto program_exp = ebpf::Program::load({}, BPF_PROG_TYPE_KPROBE, kDebug);
ASSERT_TRUE(program_exp.isError());
ASSERT_EQ(program_exp.getErrorCode(), ebpf::Program::Error::Unknown);
}
} // namespace
} // namespace osquery

View File

@ -1,38 +0,0 @@
# Copyright (c) 2014-present, The osquery authors
#
# This source code is licensed as defined by the LICENSE file found in the
# root directory of this source tree.
#
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
function(osqueryUtilsSystemLinuxPerfeventMain)
generateOsqueryUtilsSystemLinuxPerfevent()
endfunction()
function(generateOsqueryUtilsSystemLinuxPerfevent)
if(DEFINED PLATFORM_LINUX)
set(source_files
perf_event.cpp
)
add_osquery_library(osquery_utils_system_linux_perfevent EXCLUDE_FROM_ALL ${source_files})
target_link_libraries(osquery_utils_system_linux_perfevent PUBLIC
osquery_cxx_settings
osquery_utils_expected
osquery_utils_system_errno
thirdparty_boost
)
set(public_header_files
perf_event.h
)
generateIncludeNamespace(osquery_utils_system_linux_perfevent "osquery/utils/system/linux/perf_event" "FILE_ONLY" ${public_header_files})
else()
add_osquery_library(osquery_utils_system_linux_perfevent INTERFACE)
endif()
endfunction()
osqueryUtilsSystemLinuxPerfeventMain()

View File

@ -1,51 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/utils/system/linux/perf_event/perf_event.h>
#include <boost/io/detail/quoted_manip.hpp>
#include <linux/hw_breakpoint.h>
#include <linux/perf_event.h>
#include <unistd.h>
#ifndef __NR_perf_event_open
#if defined(__PPC__)
#define __NR_perf_event_open 319
#elif defined(__i386__)
#define __NR_perf_event_open 336
#elif defined(__x86_64__)
#define __NR_perf_event_open 298
#elif defined(__aarch64__)
#define __NR_perf_event_open 364
#else
#error __NR_perf_event_open must be defined
#endif
#endif
namespace osquery {
namespace perf_event_open {
Expected<int, PosixError> syscall(struct perf_event_attr* attr,
pid_t const pid,
int const cpu,
int const group_fd,
unsigned long const flags) {
auto ret = ::syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
if (ret < 0) {
return createError(to<PosixError>(errno))
<< "syscall perf_event_open failed "
<< boost::io::quoted(strerror(errno));
}
return ret;
}
} // namespace perf_event_open
} // namespace osquery

View File

@ -1,27 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/system/posix/errno.h>
#include <linux/perf_event.h>
namespace osquery {
namespace perf_event_open {
Expected<int, PosixError> syscall(struct perf_event_attr* attr,
pid_t pid,
int cpu,
int group_fd,
unsigned long const flags);
} // namespace perf_event_open
} // namespace osquery

View File

@ -1,50 +0,0 @@
# Copyright (c) 2014-present, The osquery authors
#
# This source code is licensed as defined by the LICENSE file found in the
# root directory of this source tree.
#
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
function(osqueryUtilsSystemLinuxTracingMain)
if(OSQUERY_BUILD_TESTS)
add_subdirectory("tests")
endif()
generateOsqueryUtilsSystemLinuxTracing()
endfunction()
function(generateOsqueryUtilsSystemLinuxTracing)
if(DEFINED PLATFORM_LINUX)
set(source_files
native_event.cpp
)
add_osquery_library(osquery_utils_system_linux_tracing EXCLUDE_FROM_ALL
${source_files}
)
target_link_libraries(osquery_utils_system_linux_tracing PUBLIC
osquery_cxx_settings
osquery_utils_conversions
osquery_utils_expected
osquery_logger
thirdparty_boost
thirdparty_googletest_headers
)
set(public_header_files
native_event.h
types.h
)
generateIncludeNamespace(osquery_utils_system_linux_tracing "osquery/utils/system/linux/tracing" "FILE_ONLY" ${public_header_files})
add_test(NAME osquery_utils_system_linux_tracing_tests-test COMMAND osquery_utils_system_linux_tracing_tests-test)
else()
add_osquery_library(osquery_utils_system_linux_tracing INTERFACE)
endif()
endfunction()
osqueryUtilsSystemLinuxTracingMain()

View File

@ -1,131 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <osquery/utils/system/linux/tracing/native_event.h>
#include <osquery/utils/conversions/tryto.h>
#include <osquery/logger/logger.h>
#include <boost/filesystem/path.hpp>
#include <boost/io/detail/quoted_manip.hpp>
#include <fstream>
namespace osquery {
namespace tracing {
namespace fs = boost::filesystem;
fs::path getNativeEventFullPath(const std::string& event_path) {
return fs::path("/sys/kernel/debug/tracing/events") / event_path;
}
NativeEvent::~NativeEvent() {
auto const exp = enable(false);
if (exp.isError()) {
LOG(WARNING) << "Disabling system event " << event_path_
<< " failed: " << exp.getError().getMessage();
}
}
NativeEvent::NativeEvent(NativeEvent&& other)
: id_(other.id_), event_path_(std::move(other.event_path_)) {
other.id_ = -1;
}
NativeEvent& NativeEvent::operator=(NativeEvent&& other) {
std::swap(id_, other.id_);
std::swap(event_path_, other.event_path_);
return *this;
}
Expected<NativeEvent, NativeEvent::Error> NativeEvent::load(
std::string event_path) {
auto instance = NativeEvent(std::move(event_path));
auto exp = instance.enable(true);
if (exp.isError()) {
return exp.takeError();
}
return Expected<NativeEvent, NativeEvent::Error>(std::move(instance));
}
SystemEventId NativeEvent::id() const {
return id_;
}
NativeEvent::NativeEvent(std::string event_path)
: event_path_(std::move(event_path)) {}
namespace {
Expected<int, NativeEvent::Error> extractIdFromTheSystem(
fs::path const& full_event_path) {
auto const id_path = full_event_path / "id";
auto id_in =
std::fstream(id_path.native(), std::ios_base::in | std::ios_base::binary);
auto id_str = std::string{};
if (id_in.is_open()) {
id_in >> id_str;
}
if (!id_in.is_open() || id_in.fail()) {
return createError(NativeEvent::Error::System)
<< "Could not open linux event id file "
<< boost::io::quoted(id_path.string());
}
auto id_exp = tryTo<SystemEventId>(id_str);
if (id_exp.isError()) {
return createError(NativeEvent::Error::System)
<< "Could not parse linux event id from the string "
<< boost::io::quoted(id_str);
}
return id_exp.get();
}
} // namespace
bool NativeEvent::isEnabled() const {
return id_ >= 0;
}
ExpectedSuccess<NativeEvent::Error> NativeEvent::enable(bool do_enable) {
if (do_enable == isEnabled()) {
// Nothing to do it is already enabled or disabled
return Success{};
}
auto const full_event_path = getNativeEventFullPath(event_path_);
auto const event_enable_path = full_event_path / "enable";
auto event_enable_out = std::fstream(
event_enable_path.native(), std::ios_base::out | std::ios_base::binary);
if (event_enable_out.is_open()) {
auto const buf = do_enable ? "1" : "0";
event_enable_out << buf;
}
if (!event_enable_out.is_open() || event_enable_out.fail()) {
auto const action = do_enable ? "enable" : "disable";
return createError(Error::System)
<< "Could not " << action
<< " system event, not sufficient rights to modify file "
<< boost::io::quoted(event_enable_path.string());
}
if (do_enable) {
auto id_exp = extractIdFromTheSystem(full_event_path);
if (id_exp.isError()) {
return createError(Error::System, id_exp.takeError())
<< "Could not retrieve event id from the system";
}
id_ = id_exp.take();
} else {
id_ = -1;
}
return Success{};
}
} // namespace tracing
} // namespace osquery

View File

@ -1,58 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/system/linux/tracing/types.h>
#include <string>
namespace osquery {
namespace tracing {
/**
* Wrapper for the native linux system events, see
* "/sys/kernel/debug/tracing/events/"
*/
class NativeEvent final {
public:
~NativeEvent();
NativeEvent(NativeEvent&& other);
NativeEvent& operator=(NativeEvent&& other);
NativeEvent(NativeEvent const&) = delete;
NativeEvent& operator=(NativeEvent const&) = delete;
enum class Error {
Unknown = 1,
System = 2,
};
/**
* Enable event type with path @event_path and receive the id
*/
static Expected<NativeEvent, NativeEvent::Error> load(std::string event_path);
SystemEventId id() const;
private:
explicit NativeEvent(std::string event_path);
bool isEnabled() const;
ExpectedSuccess<Error> enable(bool do_enable);
private:
SystemEventId id_ = -1;
std::string event_path_;
};
} // namespace tracing
} // namespace osquery

View File

@ -1,37 +0,0 @@
# Copyright (c) 2014-present, The osquery authors
#
# This source code is licensed as defined by the LICENSE file found in the
# root directory of this source tree.
#
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
function(osqueryUtilsSystemLinuxTracingTestsMain)
if(DEFINED PLATFORM_LINUX)
generateOsqueryUtilsSystemLinuxTracingTestsTest()
endif()
endfunction()
function(generateOsqueryUtilsSystemLinuxTracingTestsTest)
set(source_files
empty.cpp
)
if(DEFINED PLATFORM_LINUX)
set(platform_source_files
native_event.cpp
)
list(APPEND source_files ${platform_source_files})
endif()
add_osquery_executable(osquery_utils_system_linux_tracing_tests-test ${source_files})
target_link_libraries(osquery_utils_system_linux_tracing_tests-test PRIVATE
osquery_cxx_settings
osquery_utils_system_linux_tracing
thirdparty_googletest
)
endfunction()
osqueryUtilsSystemLinuxTracingTestsMain()

View File

@ -1,8 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/

View File

@ -1,25 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#include <gtest/gtest.h>
#include <osquery/utils/system/linux/tracing/native_event.h>
namespace osquery {
namespace {
class NativeEventTests : public testing::Test {};
TEST_F(NativeEventTests, non_root_load_should_fail) {
auto const exp = tracing::NativeEvent::load("syscalls/sys_enter_open");
ASSERT_TRUE(exp.isError());
ASSERT_EQ(exp.getErrorCode(), tracing::NativeEvent::Error::System);
}
} // namespace
} // namespace osquery

View File

@ -1,20 +0,0 @@
/**
* Copyright (c) 2014-present, The osquery authors
*
* This source code is licensed as defined by the LICENSE file found in the
* root directory of this source tree.
*
* SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
*/
#pragma once
#include <cstdint>
namespace osquery {
namespace tracing {
using SystemEventId = std::int64_t;
} // namespace tracing
} // namespace osquery