Skip to content

Commit

Permalink
new(libsinsp): POC falcosecurity#2 for optimized syscalls option
Browse files Browse the repository at this point in the history
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
  • Loading branch information
incertum committed Jan 11, 2023
1 parent 70d54a5 commit c4b9f5b
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 18 deletions.
50 changes: 32 additions & 18 deletions userspace/libsinsp/examples/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ std::function<void(sinsp& inspector)> dump;
static bool g_interrupted = false;
static const uint8_t g_backoff_timeout_secs = 2;
static bool g_all_threads = false;
static bool optimized_syscalls_push_down = false;
static bool json_dump_init_success = false;
string engine_string = KMOD_ENGINE; /* Default for backward compatibility. */
string filter_string = "";
Expand Down Expand Up @@ -73,17 +74,18 @@ static void usage()
Overview: Goal of sinsp-example binary is to test and debug sinsp functionality and print events to STDOUT. All drivers are supported.
Options:
-h, --help Print this page.
-f <filter>, --filter <filter> Filter string for events (see https://falco.org/docs/rules/supported-fields/ for supported fields).
-j, --json Use JSON as the output format.
-a, --all-threads Output information about all threads, not just the main one.
-b <path>, --bpf <path> BPF probe.
-m, --modern_bpf modern BPF probe.
-k, --kmod Kernel module
-s <path>, --scap_file <path> Scap file
-d <dim>, --buffer_dim <dim> Dimension in bytes that every per-CPU buffer will have.
-o <fields>, --output-fields-json <fields> [JSON support only, can also use without -j] Output fields string (see <filter> for supported display fields) that overwrites JSON default output fields for all events. * at the beginning prints JSON keys with null values, else no null fields are printed.
-E, --exclude-users Don't create the user/group tables
-h, --help Print this page.
-f <filter>, --filter <filter> Filter string for events (see https://falco.org/docs/rules/supported-fields/ for supported fields).
-j, --json Use JSON as the output format.
-a, --all-threads Output information about all threads, not just the main one.
-b <path>, --bpf <path> BPF probe.
-m, --modern_bpf Modern BPF probe.
-k, --kmod Kernel module.
-s <path>, --scap_file <path> Scap file.
-d <dim>, --buffer_dim <dim> Dimension in bytes that every per-CPU buffer will have.
-o <fields>, --output-fields-json <fields> [JSON support only, can also use without -j] Output fields string (see <filter> for supported display fields) that overwrites JSON default output fields for all events. * at the beginning prints JSON keys with null values, else no null fields are printed.
-E, --exclude-users Don't create the user/group tables.
-x, --optimized-syscalls Only trace syscalls and dependent syscalls needed for sinsp state built-up and life-cyle management.
)";
cout << usage << endl;
}
Expand All @@ -104,12 +106,13 @@ void parse_CLI_options(sinsp& inspector, int argc, char** argv)
{"buffer_dim", required_argument, 0, 'd'},
{"output-fields-json", required_argument, 0, 'o'},
{"exclude-users", no_argument, 0, 'E'},
{"optimized-syscalls", no_argument, 0, 'x'},
{0, 0, 0, 0}};

int op;
int long_index = 0;
while((op = getopt_long(argc, argv,
"hf:jab:mks:d:o:E",
"hf:jab:mks:d:o:E:x",
long_options, &long_index)) != -1)
{
switch(op)
Expand Down Expand Up @@ -151,6 +154,9 @@ void parse_CLI_options(sinsp& inspector, int argc, char** argv)
case 'E':
inspector.set_import_users(false);
break;
case 'x':
optimized_syscalls_push_down = true;
break;
default:
break;
}
Expand All @@ -160,12 +166,20 @@ void parse_CLI_options(sinsp& inspector, int argc, char** argv)

void open_engine(sinsp& inspector)
{
std::cout << "-- Try to open: '" + engine_string + "' engine." << std::endl;

/* Get only necessary tracepoints. */
std::unordered_set<uint32_t> tp_set = inspector.enforce_sinsp_state_tp();
std::unordered_set<uint32_t> ppm_sc;

if(optimized_syscalls_push_down && !inspector.m_filter_evttypes_names.empty())
{
ppm_sc = {};
printf("-- Setting optimized syscalls pushdown filters:\n");
inspector.set_filter_evttypes_ppm_sc_set(inspector.m_filter_evttypes_names, ppm_sc);
inspector.enforce_optimized_ppm_sc_set(ppm_sc, true);
}

std::cout << "-- Try to open: '" + engine_string + "' engine." << std::endl;

if(!engine_string.compare(KMOD_ENGINE))
{
inspector.open_kmod(buffer_bytes_dim, ppm_sc, tp_set);
Expand Down Expand Up @@ -283,10 +297,6 @@ int main(int argc, char** argv)
signal(SIGINT, sigint_handler);
signal(SIGTERM, sigint_handler);

open_engine(inspector);

std::cout << "-- Start capture" << std::endl;

if(!filter_string.empty())
{
try
Expand All @@ -299,6 +309,10 @@ int main(int argc, char** argv)
}
}

open_engine(inspector);

std::cout << "-- Start capture" << std::endl;

inspector.start_capture();
while(!g_interrupted)
{
Expand Down
28 changes: 28 additions & 0 deletions userspace/libsinsp/filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1677,6 +1677,30 @@ static void add_filtercheck_value(gen_event_filter_check *chk, size_t idx, const
}
}

static void add_evttypes_names(gen_event_filter_check *chk, const std::string& value, std::unordered_set<std::string>& m_filter_evttypes_names)
{
// TODO fix: Currently includes negative evt.type checks e.g. "and not evt.type in (connect, accept)"
// -> ideally would account for grammar before current check e.g. "and not"
// -> at the same time negative evt.type filter should not be allowed and/or
// fall back to simple_set in that case

switch(chk->m_cmpop)
{
case CO_BCONTAINS:
case CO_BSTARTSWITH:
case CO_NE:
case CO_LT:
case CO_LE:
case CO_GT:
case CO_GE:
case CO_NONE:
break;
default:
m_filter_evttypes_names.insert(value.c_str());
break;
}
}

void sinsp_filter_compiler::visit(libsinsp::filter::ast::binary_check_expr* e)
{
m_pos = e->get_pos();
Expand All @@ -1699,6 +1723,10 @@ void sinsp_filter_compiler::visit(libsinsp::filter::ast::binary_check_expr* e)
for (size_t i = 0; i < m_field_values.size(); i++)
{
add_filtercheck_value(check, i, m_field_values[i]);
if (field.compare("evt.type") == 0)
{
add_evttypes_names(check, m_field_values[i], m_filter_evttypes_names);
}
}
}

Expand Down
1 change: 1 addition & 0 deletions userspace/libsinsp/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class SINSP_PUBLIC sinsp_filter_compiler:
\note Throws a sinsp_exception if the filter syntax is not valid
*/
sinsp_filter* compile();
std::unordered_set<std::string> m_filter_evttypes_names = {};

const libsinsp::filter::ast::pos_info& get_pos() const { return m_pos; }

Expand Down
6 changes: 6 additions & 0 deletions userspace/libsinsp/sinsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,12 @@ void sinsp::set_filter(const std::string& filter)
sinsp_filter_compiler compiler(this, filter);
m_filter = compiler.compile();
m_filterstring = filter;
m_filter_evttypes_names = compiler.m_filter_evttypes_names;
}

std::unordered_set<std::string> sinsp::get_filter_evttypes_names()
{
return m_filter_evttypes_names;
}

const std::string sinsp::get_filter()
Expand Down
19 changes: 19 additions & 0 deletions userspace/libsinsp/sinsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,13 @@ class SINSP_PUBLIC sinsp : public capture_stats_source
*/
void set_filter(sinsp_filter* filter);

/*!
\brief Return unordered set of string names of the syscalls/events defined in the filter as evt.type.
\return \ref set_filter() has to be called first to compile and parse the filter, else return empty.
*/
std::unordered_set<std::string> get_filter_evttypes_names();

/*!
\brief Return the filter set for this capture.
Expand Down Expand Up @@ -902,6 +909,17 @@ class SINSP_PUBLIC sinsp : public capture_stats_source
*/
std::unordered_set<uint32_t> enforce_sys_ppm_sc_set(std::unordered_set<uint32_t> ppm_sc_set = {});

/*!
\brief Enforce passed set of syscalls with the ones needed for minimal
sinsp state built-up and lifecycle management (useful for Falco use case).
*/
void enforce_optimized_ppm_sc_set(std::unordered_set<uint32_t>& ppm_sc_set, bool print = false);

/*!
\brief Set ppm_sc_code for each evt.type in filter(s).
*/
void set_filter_evttypes_ppm_sc_set(std::unordered_set<std::string> m_filter_evttypes_names, std::unordered_set<uint32_t>& ppm_sc_set);

/*!
\brief Get all the available ppm_sc.
Does enforce minimum sinsp state set.
Expand Down Expand Up @@ -1335,6 +1353,7 @@ VISIBILITY_PRIVATE
uint64_t m_firstevent_ts;
sinsp_filter* m_filter;
std::string m_filterstring;
std::unordered_set<std::string> m_filter_evttypes_names;
//
// Internal stats
//
Expand Down
89 changes: 89 additions & 0 deletions userspace/libsinsp/sinsp_ppm_sc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,95 @@ std::unordered_set<uint32_t> sinsp::enforce_sys_ppm_sc_set(std::unordered_set<ui
return ppm_sc_set;
}

void sinsp::enforce_optimized_ppm_sc_set(std::unordered_set<uint32_t>& ppm_sc_set, bool print)
{
std::unordered_set<uint32_t> ppm_sc_set_filter_evttypes = ppm_sc_set;

/* Enforce syscalls solely needed for minimal sinsp state built-up and life-cycle management (Falco use case). */
std::unordered_set<uint32_t> base_thread_state_set = {
PPM_SC_CLONE, /* These syscalls are all used to build up or modify info of the basic process (tinfo) struct. */
PPM_SC_CLONE3,
PPM_SC_FORK,
PPM_SC_VFORK,
PPM_SC_EXECVE,
PPM_SC_EXECVEAT,
PPM_SC_FCHDIR,
PPM_SC_CHDIR,
PPM_SC_CHROOT,
PPM_SC_CAPSET,
PPM_SC_SETGID,
PPM_SC_SETPGID,
PPM_SC_SETRESGID,
PPM_SC_SETRESUID,
PPM_SC_SETSID,
PPM_SC_SETUID,
PPM_SC_CLOSE, /* Close syscall is crucial for destroying fds associated w/ a thread, given it's generic nature include in base set for simplicity. */
};

/* Crucial minimal PPME events that affect sinsp state other than syscalls are added later w/ the sinsp_ppm_sc APIs. */
ppm_sc_set.insert(base_thread_state_set.begin(), base_thread_state_set.end());

/* Add more dependent syscalls conditioned by syscalls / evttypes passed over the filter. */
std::unordered_set<uint32_t> base_network_set = {
PPM_SC_SOCKET,
PPM_SC_GETSOCKOPT,
};

std::unordered_set<uint32_t> base_accept_listen_set = {
PPM_SC_BIND,
};

for(const auto& ppm_sc_code : ppm_sc_set_filter_evttypes)
{
// TODO finalize
if (ppm_sc_code == PPM_SC_CONNECT ||
ppm_sc_code == PPM_SC_SOCKET ||
ppm_sc_code == PPM_SC_GETSOCKOPT ||
ppm_sc_code == PPM_SC_SETSOCKOPT ||
ppm_sc_code == PPM_SC_RECVMSG ||
ppm_sc_code == PPM_SC_RECVFROM ||
ppm_sc_code == PPM_SC_SENDMSG ||
ppm_sc_code == PPM_SC_SENDTO ||
ppm_sc_code == PPM_SC_BIND ||
ppm_sc_code == PPM_SC_SHUTDOWN ||
ppm_sc_code == PPM_SC_GETSOCKNAME ||
ppm_sc_code == PPM_SC_GETPEERNAME ||
ppm_sc_code == PPM_SC_SOCKETPAIR)
{
ppm_sc_set.insert(base_network_set.begin(), base_network_set.end());
} else if ( ppm_sc_code == PPM_SC_ACCEPT ||
ppm_sc_code == PPM_SC_ACCEPT4 ||
ppm_sc_code == PPM_SC_LISTEN)
{
ppm_sc_set.insert(base_network_set.begin(), base_network_set.end());
ppm_sc_set.insert(base_accept_listen_set.begin(), base_accept_listen_set.end());
}
}

if (print)
{
for(const auto& ppm_sc_code : ppm_sc_set)
{
std::string ppm_sc_name = g_infotables.m_syscall_info_table[ppm_sc_code].name;
printf("- %-25s ppm_code: (%d)\n", ppm_sc_name.c_str(), ppm_sc_code);
}
}

}

void sinsp::set_filter_evttypes_ppm_sc_set(std::unordered_set<std::string> m_filter_evttypes_names, std::unordered_set<uint32_t>& ppm_sc_set)
{
// TODO possibly change lookup table / lookup procedure as tables get refactored
for (int ppm_sc_code = 0; ppm_sc_code < PPM_SC_MAX; ++ppm_sc_code)
{
std::string ppm_sc_name = g_infotables.m_syscall_info_table[ppm_sc_code].name;
if (m_filter_evttypes_names.find(ppm_sc_name) != m_filter_evttypes_names.end())
{
ppm_sc_set.insert(ppm_sc_code);
}
}
}

std::unordered_set<uint32_t> sinsp::get_event_set_from_ppm_sc_set(const std::unordered_set<uint32_t> &ppm_sc_set)
{
std::vector<uint32_t> events_array(PPM_EVENT_MAX, 0);
Expand Down

0 comments on commit c4b9f5b

Please sign in to comment.