Merge pull request #16675 from topimiettinen/exec-syscall-error-action

SystemCallFilter, SystemCallErrorAction error actions kill and log
This commit is contained in:
Lennart Poettering
2020-09-16 17:01:44 +02:00
committed by GitHub
18 changed files with 375 additions and 19 deletions
+6 -1
View File
@@ -16,6 +16,9 @@
#include "missing_network.h"
#include "parse-util.h"
#include "process-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
@@ -314,6 +317,7 @@ int parse_errno(const char *t) {
return e;
}
#if HAVE_SECCOMP
int parse_syscall_and_errno(const char *in, char **name, int *error) {
_cleanup_free_ char *n = NULL;
char *p;
@@ -332,7 +336,7 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
p = strchr(in, ':');
if (p) {
e = parse_errno(p + 1);
e = seccomp_parse_errno_or_action(p + 1);
if (e < 0)
return e;
@@ -351,6 +355,7 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
return 0;
}
#endif
static const char *mangle_base(const char *s, unsigned *base) {
const char *k;
+2
View File
@@ -19,7 +19,9 @@ int parse_mtu(int family, const char *s, uint32_t *ret);
int parse_size(const char *t, uint64_t base, uint64_t *size);
int parse_range(const char *t, unsigned *lower, unsigned *upper);
int parse_errno(const char *t);
#if HAVE_SECCOMP
int parse_syscall_and_errno(const char *in, char **name, int *error);
#endif
#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30)
#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29)
+125 -2
View File
@@ -387,7 +387,7 @@ static int property_get_syscall_filter(
continue;
if (num >= 0) {
e = errno_to_name(num);
e = seccomp_errno_or_action_to_string(num);
if (e) {
s = strjoin(name, ":", e);
if (!s)
@@ -415,6 +415,58 @@ static int property_get_syscall_filter(
return sd_bus_message_close_container(reply);
}
static int property_get_syscall_log(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
ExecContext *c = userdata;
_cleanup_strv_free_ char **l = NULL;
int r;
#if HAVE_SECCOMP
void *id, *val;
#endif
assert(bus);
assert(reply);
assert(c);
r = sd_bus_message_open_container(reply, 'r', "bas");
if (r < 0)
return r;
r = sd_bus_message_append(reply, "b", c->syscall_log_allow_list);
if (r < 0)
return r;
#if HAVE_SECCOMP
HASHMAP_FOREACH_KEY(val, id, c->syscall_log) {
char *name = NULL;
name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
if (!name)
continue;
r = strv_consume(&l, name);
if (r < 0)
return r;
}
#endif
strv_sort(l);
r = sd_bus_message_append_strv(reply, l);
if (r < 0)
return r;
return sd_bus_message_close_container(reply);
}
static int property_get_syscall_archs(
sd_bus *bus,
const char *path,
@@ -1068,6 +1120,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("SystemCallFilter", "(bas)", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SystemCallArchitectures", "as", property_get_syscall_archs, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SystemCallErrorNumber", "i", bus_property_get_int, offsetof(ExecContext, syscall_errno), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SystemCallLog", "(bas)", property_get_syscall_log, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Personality", "s", property_get_personality, offsetof(ExecContext, personality), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LockPersonality", "b", bus_property_get_bool, offsetof(ExecContext, lock_personality), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RestrictAddressFamilies", "(bas)", property_get_address_families, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1424,7 +1477,7 @@ static const char* mount_propagation_flags_to_string_with_check(unsigned long n)
static BUS_DEFINE_SET_TRANSIENT(nsec, "t", uint64_t, nsec_t, NSEC_FMT);
static BUS_DEFINE_SET_TRANSIENT_IS_VALID(log_level, "i", int32_t, int, "%" PRIi32, log_level_is_valid);
#if HAVE_SECCOMP
static BUS_DEFINE_SET_TRANSIENT_IS_VALID(errno, "i", int32_t, int, "%" PRIi32, errno_is_valid);
static BUS_DEFINE_SET_TRANSIENT_IS_VALID(errno, "i", int32_t, int, "%" PRIi32, seccomp_errno_or_action_is_valid);
#endif
static BUS_DEFINE_SET_TRANSIENT_PARSE(std_input, ExecInput, exec_input_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(std_output, ExecOutput, exec_output_from_string);
@@ -2230,6 +2283,76 @@ int bus_exec_context_set_transient_property(
return 1;
} else if (streq(name, "SystemCallLog")) {
int allow_list;
_cleanup_strv_free_ char **l = NULL;
r = sd_bus_message_enter_container(message, 'r', "bas");
if (r < 0)
return r;
r = sd_bus_message_read(message, "b", &allow_list);
if (r < 0)
return r;
r = sd_bus_message_read_strv(message, &l);
if (r < 0)
return r;
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
_cleanup_free_ char *joined = NULL;
SeccompParseFlags invert_flag = allow_list ? 0 : SECCOMP_PARSE_INVERT;
char **s;
if (strv_isempty(l)) {
c->syscall_log_allow_list = false;
c->syscall_log = hashmap_free(c->syscall_log);
unit_write_settingf(u, flags, name, "SystemCallLog=");
return 1;
}
if (!c->syscall_log) {
c->syscall_log = hashmap_new(NULL);
if (!c->syscall_log)
return log_oom();
c->syscall_log_allow_list = allow_list;
}
STRV_FOREACH(s, l) {
_cleanup_free_ char *n = NULL;
int e;
r = parse_syscall_and_errno(*s, &n, &e);
if (r < 0)
return r;
r = seccomp_parse_syscall_filter(n,
0, /* errno not used */
c->syscall_log,
SECCOMP_PARSE_LOG | SECCOMP_PARSE_PERMISSIVE |
invert_flag |
(c->syscall_log_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
u->id,
NULL, 0);
if (r < 0)
return r;
}
joined = strv_join(l, " ");
if (!joined)
return -ENOMEM;
unit_write_settingf(u, flags, name, "SystemCallLog=%s%s", allow_list ? "" : "~", joined);
}
return 1;
} else if (streq(name, "SystemCallArchitectures")) {
_cleanup_strv_free_ char **l = NULL;
+60 -5
View File
@@ -1407,6 +1407,13 @@ static bool context_has_syscall_filters(const ExecContext *c) {
!hashmap_isempty(c->syscall_filter);
}
static bool context_has_syscall_logs(const ExecContext *c) {
assert(c);
return c->syscall_log_allow_list ||
!hashmap_isempty(c->syscall_log);
}
static bool context_has_no_new_privileges(const ExecContext *c) {
assert(c);
@@ -1428,6 +1435,7 @@ static bool context_has_no_new_privileges(const ExecContext *c) {
c->protect_kernel_logs ||
c->private_devices ||
context_has_syscall_filters(c) ||
context_has_syscall_logs(c) ||
!set_isempty(c->syscall_archs) ||
c->lock_personality ||
c->protect_hostname;
@@ -1465,7 +1473,7 @@ static int apply_syscall_filter(const Unit* u, const ExecContext *c, bool needs_
if (skip_seccomp_unavailable(u, "SystemCallFilter="))
return 0;
negative_action = c->syscall_errno == 0 ? scmp_act_kill_process() : SCMP_ACT_ERRNO(c->syscall_errno);
negative_action = c->syscall_errno == SECCOMP_ERROR_NUMBER_KILL ? scmp_act_kill_process() : SCMP_ACT_ERRNO(c->syscall_errno);
if (c->syscall_allow_list) {
default_action = negative_action;
@@ -1484,6 +1492,39 @@ static int apply_syscall_filter(const Unit* u, const ExecContext *c, bool needs_
return seccomp_load_syscall_filter_set_raw(default_action, c->syscall_filter, action, false);
}
static int apply_syscall_log(const Unit* u, const ExecContext *c) {
#ifdef SCMP_ACT_LOG
uint32_t default_action, action;
#endif
assert(u);
assert(c);
if (!context_has_syscall_logs(c))
return 0;
#ifdef SCMP_ACT_LOG
if (skip_seccomp_unavailable(u, "SystemCallLog="))
return 0;
if (c->syscall_log_allow_list) {
/* Log nothing but the ones listed */
default_action = SCMP_ACT_ALLOW;
action = SCMP_ACT_LOG;
} else {
/* Log everything but the ones listed */
default_action = SCMP_ACT_LOG;
action = SCMP_ACT_ALLOW;
}
return seccomp_load_syscall_filter_set_raw(default_action, c->syscall_log, action, false);
#else
/* old libseccomp */
log_unit_debug(u, "SECCOMP feature SCMP_ACT_LOG not available, skipping SystemCallLog=");
return 0;
#endif
}
static int apply_syscall_archs(const Unit *u, const ExecContext *c) {
assert(u);
assert(c);
@@ -4438,6 +4479,12 @@ static int exec_child(
return log_unit_error_errno(unit, r, "Failed to lock personalities: %m");
}
r = apply_syscall_log(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
return log_unit_error_errno(unit, r, "Failed to apply system call log filters: %m");
}
/* This really should remain the last step before the execve(), to make sure our own code is unaffected
* by the filter as little as possible. */
r = apply_syscall_filter(unit, context, needs_ambient_hack);
@@ -4675,6 +4722,9 @@ void exec_context_init(ExecContext *c) {
assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL;
c->log_level_max = -1;
#if HAVE_SECCOMP
c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
#endif
numa_policy_reset(&c->numa_policy);
}
@@ -5474,7 +5524,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
fputs(strna(name), f);
if (num >= 0) {
errno_name = errno_to_name(num);
errno_name = seccomp_errno_or_action_to_string(num);
if (errno_name)
fprintf(f, ":%s", errno_name);
else
@@ -5517,15 +5567,20 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
prefix, c->network_namespace_path);
if (c->syscall_errno > 0) {
#if HAVE_SECCOMP
const char *errno_name;
#endif
fprintf(f, "%sSystemCallErrorNumber: ", prefix);
errno_name = errno_to_name(c->syscall_errno);
#if HAVE_SECCOMP
errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
if (errno_name)
fprintf(f, "%s\n", errno_name);
fputs(errno_name, f);
else
fprintf(f, "%d\n", c->syscall_errno);
fprintf(f, "%d", c->syscall_errno);
#endif
fputc('\n', f);
}
for (size_t i = 0; i < c->n_mount_images; i++) {
+3
View File
@@ -302,6 +302,9 @@ struct ExecContext {
int syscall_errno;
bool syscall_allow_list:1;
Hashmap *syscall_log;
bool syscall_log_allow_list:1; /* Log listed system calls */
bool address_families_allow_list:1;
Set *address_families;
+2
View File
@@ -79,6 +79,7 @@ m4_ifdef(`HAVE_SECCOMP',
`$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context)
$1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context.syscall_archs)
$1.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof($1, exec_context)
$1.SystemCallLog, config_parse_syscall_log, 0, offsetof($1, exec_context)
$1.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof($1, exec_context.memory_deny_write_execute)
$1.RestrictNamespaces, config_parse_restrict_namespaces, 0, offsetof($1, exec_context)
$1.RestrictRealtime, config_parse_bool, 0, offsetof($1, exec_context.restrict_realtime)
@@ -88,6 +89,7 @@ $1.LockPersonality, config_parse_bool, 0,
`$1.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.SystemCallLog, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.MemoryDenyWriteExecute, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.RestrictNamespaces, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.RestrictRealtime, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+83 -2
View File
@@ -3197,6 +3197,86 @@ int config_parse_syscall_filter(
}
}
int config_parse_syscall_log(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
_unused_ const Unit *u = userdata;
bool invert = false;
const char *p;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(u);
if (isempty(rvalue)) {
/* Empty assignment resets the list */
c->syscall_log = hashmap_free(c->syscall_log);
c->syscall_log_allow_list = false;
return 0;
}
if (rvalue[0] == '~') {
invert = true;
rvalue++;
}
if (!c->syscall_log) {
c->syscall_log = hashmap_new(NULL);
if (!c->syscall_log)
return log_oom();
if (invert)
/* Log everything but the ones listed */
c->syscall_log_allow_list = false;
else
/* Log nothing but the ones listed */
c->syscall_log_allow_list = true;
}
p = rvalue;
for (;;) {
_cleanup_free_ char *word = NULL, *name = NULL;
int num;
r = extract_first_word(&p, &word, NULL, 0);
if (r == 0)
return 0;
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
return 0;
}
r = parse_syscall_and_errno(word, &name, &num);
if (r < 0 || num >= 0) { /* errno code not allowed */
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse syscall, ignoring: %s", word);
continue;
}
r = seccomp_parse_syscall_filter(
name, 0, c->syscall_log,
SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
(invert ? SECCOMP_PARSE_INVERT : 0)|
(c->syscall_log_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
unit, filename, line);
if (r < 0)
return r;
}
}
int config_parse_syscall_archs(
const char *unit,
const char *filename,
@@ -3264,9 +3344,9 @@ int config_parse_syscall_errno(
assert(lvalue);
assert(rvalue);
if (isempty(rvalue)) {
if (isempty(rvalue) || streq(rvalue, "kill")) {
/* Empty assignment resets to KILL */
c->syscall_errno = 0;
c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
return 0;
}
@@ -5444,6 +5524,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_syscall_filter, "SYSCALLS" },
{ config_parse_syscall_archs, "ARCHS" },
{ config_parse_syscall_errno, "ERRNO" },
{ config_parse_syscall_log, "SYSCALLS" },
{ config_parse_address_families, "FAMILIES" },
{ config_parse_restrict_namespaces, "NAMESPACES" },
#endif
+1
View File
@@ -65,6 +65,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_unit_requires_mounts_for);
CONFIG_PARSER_PROTOTYPE(config_parse_syscall_filter);
CONFIG_PARSER_PROTOTYPE(config_parse_syscall_archs);
CONFIG_PARSER_PROTOTYPE(config_parse_syscall_errno);
CONFIG_PARSER_PROTOTYPE(config_parse_syscall_log);
CONFIG_PARSER_PROTOTYPE(config_parse_environ);
CONFIG_PARSER_PROTOTYPE(config_parse_pass_environ);
CONFIG_PARSER_PROTOTYPE(config_parse_unset_environ);
+10 -3
View File
@@ -30,6 +30,9 @@
#include "path-util.h"
#include "process-util.h"
#include "rlimit-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#include "securebits-util.h"
#include "signal-util.h"
#include "socket-util.h"
@@ -107,7 +110,10 @@ DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string);
DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string);
DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string);
DEFINE_BUS_APPEND_PARSE("i", log_level_from_string);
DEFINE_BUS_APPEND_PARSE("i", parse_errno);
#if !HAVE_SECCOMP
static inline int seccomp_parse_errno_or_action(const char *eq) { return -EINVAL; }
#endif
DEFINE_BUS_APPEND_PARSE("i", seccomp_parse_errno_or_action);
DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string);
DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string);
DEFINE_BUS_APPEND_PARSE("i", signal_from_string);
@@ -927,7 +933,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
return bus_append_parse_nice(m, field, eq);
if (streq(field, "SystemCallErrorNumber"))
return bus_append_parse_errno(m, field, eq);
return bus_append_seccomp_parse_errno_or_action(m, field, eq);
if (streq(field, "IOSchedulingClass"))
return bus_append_ioprio_class_from_string(m, field, eq);
@@ -1293,7 +1299,8 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
}
if (STR_IN_SET(field, "RestrictAddressFamilies",
"SystemCallFilter")) {
"SystemCallFilter",
"SystemCallLog")) {
int allow_list = 1;
const char *p = eq;
+7 -1
View File
@@ -1071,7 +1071,13 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u
int id = PTR_TO_INT(syscall_id) - 1;
int error = PTR_TO_INT(val);
if (action != SCMP_ACT_ALLOW && error >= 0)
if (error == SECCOMP_ERROR_NUMBER_KILL)
a = scmp_act_kill_process();
#ifdef SCMP_ACT_LOG
else if (action == SCMP_ACT_LOG)
a = SCMP_ACT_LOG;
#endif
else if (action != SCMP_ACT_ALLOW && error >= 0)
a = SCMP_ACT_ERRNO(error);
r = seccomp_rule_add_exact(seccomp, a, id, 0);
+25
View File
@@ -5,7 +5,10 @@
#include <stdbool.h>
#include <stdint.h>
#include "errno-list.h"
#include "parse-util.h"
#include "set.h"
#include "string-util.h"
const char* seccomp_arch_to_string(uint32_t c);
int seccomp_arch_from_string(const char *n, uint32_t *ret);
@@ -115,3 +118,25 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(scmp_filter_ctx, seccomp_release);
int parse_syscall_archs(char **l, Set **ret_archs);
uint32_t scmp_act_kill_process(void);
/* This is a special value to be used where syscall filters otherwise expect errno numbers, will be
replaced with real seccomp action. */
enum {
SECCOMP_ERROR_NUMBER_KILL = INT_MAX - 1,
};
static inline bool seccomp_errno_or_action_is_valid(int n) {
return n == SECCOMP_ERROR_NUMBER_KILL || errno_is_valid(n);
}
static inline int seccomp_parse_errno_or_action(const char *p) {
if (streq_ptr(p, "kill"))
return SECCOMP_ERROR_NUMBER_KILL;
return parse_errno(p);
}
static inline const char *seccomp_errno_or_action_to_string(int num) {
if (num == SECCOMP_ERROR_NUMBER_KILL)
return "kill";
return errno_to_name(num);
}
+1 -1
View File
@@ -4822,7 +4822,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
return 1;
} else if (STR_IN_SET(name, "SystemCallFilter", "RestrictAddressFamilies")) {
} else if (STR_IN_SET(name, "SystemCallFilter", "SystemCallLog", "RestrictAddressFamilies")) {
_cleanup_strv_free_ char **l = NULL;
int allow_list;
+2
View File
@@ -434,6 +434,8 @@ static void test_exec_systemcallfilter(Manager *m) {
test(__func__, m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-with-errno-multi.service", errno_from_name("EILSEQ"), CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-override-error-action.service", SIGSYS, CLD_KILLED);
test(__func__, m, "exec-systemcallfilter-override-error-action2.service", errno_from_name("EILSEQ"), CLD_EXITED);
#endif
}
+11 -1
View File
@@ -10,6 +10,9 @@
#include "log.h"
#include "parse-util.h"
#include "string-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
static void test_parse_boolean(void) {
assert_se(parse_boolean("1") == 1);
@@ -852,6 +855,7 @@ static void test_parse_errno(void) {
}
static void test_parse_syscall_and_errno(void) {
#if HAVE_SECCOMP
_cleanup_free_ char *n = NULL;
int e;
@@ -882,11 +886,16 @@ static void test_parse_syscall_and_errno(void) {
assert_se(e == 255);
n = mfree(n);
assert_se(parse_syscall_and_errno("hoge:kill", &n, &e) >= 0);
assert_se(streq(n, "hoge"));
assert_se(e == SECCOMP_ERROR_NUMBER_KILL);
n = mfree(n);
/* The function checks the syscall name is empty or not. */
assert_se(parse_syscall_and_errno("", &n, &e) == -EINVAL);
assert_se(parse_syscall_and_errno(":255", &n, &e) == -EINVAL);
/* errno must be a valid errno name or number between 0 and ERRNO_MAX == 4095 */
/* errno must be a valid errno name or number between 0 and ERRNO_MAX == 4095, or "kill" */
assert_se(parse_syscall_and_errno("hoge:4096", &n, &e) == -ERANGE);
assert_se(parse_syscall_and_errno("hoge:-3", &n, &e) == -ERANGE);
assert_se(parse_syscall_and_errno("hoge:12.3", &n, &e) == -EINVAL);
@@ -896,6 +905,7 @@ static void test_parse_syscall_and_errno(void) {
assert_se(parse_syscall_and_errno("hoge:-EINVAL", &n, &e) == -EINVAL);
assert_se(parse_syscall_and_errno("hoge:EINVALaaa", &n, &e) == -EINVAL);
assert_se(parse_syscall_and_errno("hoge:", &n, &e) == -EINVAL);
#endif
}
static void test_parse_mtu(void) {