mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-06 01:25:20 +00:00
Remove unused/experimental ebpf code (#6879)
This commit is contained in:
parent
4995a238c5
commit
7cbc19038e
@ -6,4 +6,3 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
|
||||
|
||||
add_subdirectory("events_stream")
|
||||
add_subdirectory("tracing")
|
||||
|
@ -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()
|
@ -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()
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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()
|
@ -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)
|
||||
*/
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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()
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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>
|
@ -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
|
@ -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
|
@ -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
|
@ -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()
|
@ -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
|
@ -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)
|
||||
*/
|
@ -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
|
@ -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
|
@ -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
|
@ -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()
|
@ -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
|
@ -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
|
@ -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()
|
@ -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
|
@ -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
|
@ -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()
|
@ -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)
|
||||
*/
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user