Skip to content

Commit

Permalink
Linux kill() enter/exit ebpf programs definitions (osquery#5386)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: osquery#5386

 Part of a linux  tracing system, blueprint: [osquery#5218](osquery#5218)

Reviewed By: SAlexandru

Differential Revision: D13654124

fbshipit-source-id: 8db63e584bd772132c1ba1c80853c60613e8036a
  • Loading branch information
akindyakov authored and Nick Anderson committed Feb 1, 2019
1 parent c3c0e5f commit 2c2d415
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 0 deletions.
4 changes: 4 additions & 0 deletions osquery/events/linux/probes/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ osquery_cxx_library(
LINUX,
[
"ebpf_tracepoint.h",
"syscall_event.h",
"syscalls_programs.h",
],
),
],
Expand All @@ -27,6 +29,8 @@ osquery_cxx_library(
LINUX,
[
"ebpf_tracepoint.cpp",
"syscall_event.cpp",
"syscalls_programs.cpp",
],
),
],
Expand Down
15 changes: 15 additions & 0 deletions osquery/events/linux/probes/syscall_event.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the Apache 2.0 license (found in the
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
* in the COPYING file in the root directory of this source tree).
* You may select, at your option, one of the above-listed licenses.
*/

#include <osquery/events/linux/probes/syscall_event.h>

namespace osquery {
namespace events {} // namespace events
} // namespace osquery
49 changes: 49 additions & 0 deletions osquery/events/linux/probes/syscall_event.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the Apache 2.0 license (found in the
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
* in the COPYING file in the root directory of this source tree).
* You may select, at your option, one of the above-listed licenses.
*/

#pragma once

#include <osquery/utils/expected/expected.h>
#include <osquery/utils/system/linux/ebpf/program.h>

namespace osquery {
namespace events {

namespace syscall {

enum class Type : __s32 {
Unknown = 0,
KillEnter = 1,
};

static constexpr std::size_t kCommSize = 16u;

struct Event {
Type type; // ebpf offset depends on the struct type in the unit
__s32 pid;
__s32 tgid;

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;
} body;
};

} // namespace syscall
} // namespace events
} // namespace osquery
111 changes: 111 additions & 0 deletions osquery/events/linux/probes/syscalls_programs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the Apache 2.0 license (found in the
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
* in the COPYING file in the root directory of this source tree).
* You may select, at your option, one of the above-listed licenses.
*/

#include <osquery/events/linux/probes/syscalls_programs.h>

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::Type) + 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::Type::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
}

} // namespace events
} // namespace osquery
28 changes: 28 additions & 0 deletions osquery/events/linux/probes/syscalls_programs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the Apache 2.0 license (found in the
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
* in the COPYING file in the root directory of this source tree).
* You may select, at your option, one of the above-listed licenses.
*/

#pragma once

#include <osquery/events/linux/probes/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> genLinuxKillEnterProgram(
enum bpf_prog_type prog_type, PerfEventCpuMap const& cpu_map);

} // namespace events
} // namespace osquery

0 comments on commit 2c2d415

Please sign in to comment.