len = read(fd, buf, sizeof(buf));
close(fd); if (len < 0) return -1; if (len >= (ssize_t)sizeof(buf)) return -1;
buf[len] = '\0';
return strtol(buf, NULL, 0);
}
staticlong read_procfs(constchar *path)
{ char *endptr, *line = NULL;
size_t len = 0;
FILE *fd; long res;
fd = fopen(path, "r"); if (!fd) return -1;
res = getline(&line, &len, fd);
fclose(fd); if (res < 0) return -1;
errno = 0;
res = strtol(line, &endptr, 10); if (errno || *line == '\0' || *endptr != '\n')
res = -1;
free(line);
return res;
}
staticvoid probe_unprivileged_disabled(void)
{ long res;
/* No support for C-style output */
res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled"); if (json_output) {
jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res);
} else { switch (res) { case 0:
printf("bpf() syscall for unprivileged users is enabled\n"); break; case 1:
printf("bpf() syscall restricted to privileged users (without recovery)\n"); break; case 2:
printf("bpf() syscall restricted to privileged users (admin can change)\n"); break; case -1:
printf("Unable to retrieve required privileges for bpf() syscall\n"); break; default:
printf("bpf() syscall restriction has unknown value %ld\n", res);
}
}
}
staticvoid probe_jit_enable(void)
{ long res;
/* No support for C-style output */
res = read_procfs("/proc/sys/net/core/bpf_jit_enable"); if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_enable", res);
} else { switch (res) { case 0:
printf("JIT compiler is disabled\n"); break; case 1:
printf("JIT compiler is enabled\n"); break; case 2:
printf("JIT compiler is enabled with debugging traces in kernel logs\n"); break; case -1:
printf("Unable to retrieve JIT-compiler status\n"); break; default:
printf("JIT-compiler status has unknown value %ld\n",
res);
}
}
}
staticvoid probe_jit_harden(void)
{ long res;
/* No support for C-style output */
res = read_procfs("/proc/sys/net/core/bpf_jit_harden"); if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_harden", res);
} else { switch (res) { case 0:
printf("JIT compiler hardening is disabled\n"); break; case 1:
printf("JIT compiler hardening is enabled for unprivileged users\n"); break; case 2:
printf("JIT compiler hardening is enabled for all users\n"); break; case -1:
printf("Unable to retrieve JIT hardening status\n"); break; default:
printf("JIT hardening status has unknown value %ld\n",
res);
}
}
}
staticvoid probe_jit_kallsyms(void)
{ long res;
/* No support for C-style output */
res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms"); if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res);
} else { switch (res) { case 0:
printf("JIT compiler kallsyms exports are disabled\n"); break; case 1:
printf("JIT compiler kallsyms exports are enabled for root\n"); break; case -1:
printf("Unable to retrieve JIT kallsyms export status\n"); break; default:
printf("JIT kallsyms exports status has unknown value %ld\n", res);
}
}
}
staticvoid probe_jit_limit(void)
{ long res;
/* No support for C-style output */
res = read_procfs("/proc/sys/net/core/bpf_jit_limit"); if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_limit", res);
} else { switch (res) { case -1:
printf("Unable to retrieve global memory limit for JIT compiler for unprivileged users\n"); break; default:
printf("Global memory limit for JIT compiler for unprivileged users is %ld bytes\n", res);
}
}
}
if (!file) { /* Some distributions build with CONFIG_IKCONFIG=y and put the * config file at /proc/config.gz.
*/
file = gzopen("/proc/config.gz", "r");
} if (!file) {
p_info("skipping kernel config, can't open file: %s",
strerror(errno)); goto end_parse;
} /* Sanity checks */ if (!gzgets(file, buf, sizeof(buf)) ||
!gzgets(file, buf, sizeof(buf))) {
p_info("skipping kernel config, can't read from file: %s",
strerror(errno)); goto end_parse;
} if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
p_info("skipping kernel config, can't find correct file"); goto end_parse;
}
while (read_next_kernel_config_option(file, buf, sizeof(buf), &value)) { for (i = 0; i < ARRAY_SIZE(options); i++) { if ((define_prefix && !options[i].macro_dump) ||
values[i] || strcmp(buf, options[i].name)) continue;
values[i] = strdup(value);
}
}
for (i = 0; i < ARRAY_SIZE(options); i++) { if (define_prefix && !options[i].macro_dump) continue;
print_kernel_option(options[i].name, values[i], define_prefix);
free(values[i]);
}
if (ifindex) { switch (prog_type) { case BPF_PROG_TYPE_SCHED_CLS: case BPF_PROG_TYPE_XDP: break; default: return;
}
res = probe_prog_type_ifindex(prog_type, ifindex);
} else {
res = libbpf_probe_bpf_prog_type(prog_type, NULL) > 0;
}
#ifdef USE_LIBCAP /* Probe may succeed even if program load fails, for unprivileged users * check that we did not fail because of insufficient permissions
*/ if (run_as_unprivileged && errno == EPERM)
res = false; #endif
supported_types[prog_type] |= res;
maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1; if (strlen(prog_type_str) > maxlen) {
p_info("program type name too long"); return;
}
probe_prog_load_ifindex(prog_type, insns, ARRAY_SIZE(insns), buf, sizeof(buf), ifindex);
res = !grep(buf, "invalid func ") && !grep(buf, "unknown func ") &&
!grep(buf, "program of this type cannot use helper ");
switch (get_vendor_id(ifindex)) { case 0x19ee: /* Netronome specific */
res = res && !grep(buf, "not supported by FW") &&
!grep(buf, "unsupported function id"); break; default: break;
}
if (supported_type) { if (ifindex)
res = probe_helper_ifindex(id, prog_type, ifindex); else
res = libbpf_probe_bpf_helper(prog_type, id, NULL) > 0; #ifdef USE_LIBCAP /* Probe may succeed even if program load fails, for * unprivileged users check that we did not fail because of * insufficient permissions
*/ if (run_as_unprivileged && errno == EPERM)
res = false; #endif
}
if (json_output) { if (res)
jsonw_string(json_wtr, helper_name[id]);
} elseif (define_prefix) {
printf("#define %sBPF__PROG_TYPE_%s__HELPER_%s %s\n",
define_prefix, ptype_name, helper_name[id],
res ? "1" : "0");
} else { if (res)
printf("\n\t- %s", helper_name[id]);
}
if (ifindex) /* Only test helpers for offload-able program types */ switch (prog_type) { case BPF_PROG_TYPE_SCHED_CLS: case BPF_PROG_TYPE_XDP: break; default: return;
}
if (json_output) {
sprintf(feat_name, "%s_available_helpers", prog_type_str);
jsonw_name(json_wtr, feat_name);
jsonw_start_array(json_wtr);
} elseif (!define_prefix) {
printf("eBPF helpers supported for program type %s:",
prog_type_str);
}
for (id = 1; id < ARRAY_SIZE(helper_name); id++) { /* Skip helper functions which emit dmesg messages when not in * the full mode.
*/ switch (id) { case BPF_FUNC_trace_printk: case BPF_FUNC_trace_vprintk: case BPF_FUNC_probe_write_user: if (!full_mode) continue;
fallthrough; default:
probe_res |= probe_helper_for_progtype(prog_type, supported_type,
define_prefix, id, prog_type_str,
ifindex);
}
}
if (json_output)
jsonw_end_array(json_wtr); elseif (!define_prefix) {
printf("\n"); if (!probe_res) { if (!supported_type)
printf("\tProgram type not supported\n"); else
printf("\tCould not determine which helpers are available\n");
}
}
print_start_section("syscall_config", "Scanning system call availability...", "/*** System call availability ***/",
define_prefix);
res = probe_bpf_syscall(define_prefix);
print_end_section();
print_start_section("program_types", "Scanning eBPF program types...", "/*** eBPF program types ***/",
define_prefix);
while (true) {
prog_type++;
prog_type_str = libbpf_bpf_prog_type_str(prog_type); /* libbpf will return NULL for variants unknown to it. */ if (!prog_type_str) break;
while (true) {
map_type++;
map_type_str = libbpf_bpf_map_type_str(map_type); /* libbpf will return NULL for variants unknown to it. */ if (!map_type_str) break;
if (define_prefix)
printf("/*\n" " * Use %sHAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)\n" " * to determine if <helper_name> is available for <prog_type_name>,\n" " * e.g.\n" " * #if %sHAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)\n" " * // do stuff with this helper\n" " * #elif\n" " * // use a workaround\n" " * #endif\n" " */\n" "#define %sHAVE_PROG_TYPE_HELPER(prog_type, helper) \\\n" " %sBPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper\n",
define_prefix, define_prefix, define_prefix,
define_prefix); while (true) {
prog_type++;
prog_type_str = libbpf_bpf_prog_type_str(prog_type); /* libbpf will return NULL for variants unknown to it. */ if (!prog_type_str) break;
if (cap_sys_admin_only) /* System does not know about CAP_BPF, meaning that * CAP_SYS_ADMIN is the only capability required. We * just checked it, break.
*/ break;
}
if ((run_as_unprivileged && !nb_bpf_caps) ||
(!run_as_unprivileged && nb_bpf_caps == ARRAY_SIZE(bpf_caps)) ||
(!run_as_unprivileged && cap_sys_admin_only && nb_bpf_caps)) { /* We are all good, exit now */
res = 0; goto exit_free;
}
if (!run_as_unprivileged) { if (cap_sys_admin_only)
p_err("missing %s, required for full feature probing; run as root or use 'unprivileged'",
bpf_caps[0].name); else
p_err("missing %s%s%s%s%s%s%s%srequired for full feature probing; run as root or use 'unprivileged'",
capability_msg(bpf_caps, 0), #ifdef CAP_BPF
capability_msg(bpf_caps, 1),
capability_msg(bpf_caps, 2),
capability_msg(bpf_caps, 3) #else "", "", "", "", "", "" #endif/* CAP_BPF */
); goto exit_free;
}
/* if (run_as_unprivileged && nb_bpf_caps > 0), drop capabilities. */ if (cap_set_flag(caps, CAP_EFFECTIVE, nb_bpf_caps, cap_list,
CAP_CLEAR)) {
p_err("bug: failed to clear capabilities: %s", strerror(errno)); goto exit_free;
}
if (cap_set_proc(caps)) {
p_err("failed to drop capabilities: %s", strerror(errno)); goto exit_free;
}
res = 0;
exit_free: if (cap_free(caps) && !res) {
p_err("failed to clear storage object for capabilities: %s",
strerror(errno));
res = -1;
}
return res; #else /* Detection assumes user has specific privileges. * We do not use libcap so let's approximate, and restrict usage to * root user only.
*/ if (geteuid()) {
p_err("full feature probing requires root privileges"); return -1;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.