Skip to content

Commit 0cad325

Browse files
eddyz87Kernel Patches Daemon
authored andcommitted
selftests/bpf: more precise cpu_mitigations state detection
test_progs and test_verifier binaries execute unpriv tests under the following conditions: - unpriv BPF is enabled; - CPU mitigations are enabled (see [1] for details). The detection of the "mitigations enabled" state is performed by unpriv_helpers.c:get_mitigations_off() via inspecting kernel boot command line, looking for a parameter "mitigations=off". Such detection scheme won't work for certain configurations, e.g. when CONFIG_CPU_MITIGATIONS is disabled and boot parameter is not supplied. Miss-detection leads to test_progs executing tests meant to be run only with mitigations enabled, e.g. verifier_and.c:known_subreg_with_unknown_reg(), and reporting false failures. Internally, verifier sets bpf_verifier_env->bypass_spec_{v1,v4} basing on the value returned by kernel/cpu.c:cpu_mitigations_off(). This function is backed by a variable kernel/cpu.c:cpu_mitigations. This state is not fully introspect-able via sysfs. The closest proxy is /sys/devices/system/cpu/vulnerabilities/spectre_v1, but it reports "vulnerable" state only if mitigations are disabled *and* current cpu is vulnerable, while verifier does not check cpu state. There are only two ways the kernel/cpu.c:cpu_mitigations can be set: - via boot parameter; - via CONFIG_CPU_MITIGATIONS option. This commit updates unpriv_helpers.c:get_mitigations_off() to scan /boot/config-$(uname -r) and /proc/config.gz for CONFIG_CPU_MITIGATIONS value in addition to boot command line check. Tested using the following configurations: - mitigations enabled (unpriv tests are enabled) - mitigations disabled via boot cmdline (unpriv tests skipped) - mitigations disabled via CONFIG_CPU_MITIGATIONS (unpriv tests skipped) [1] https://lore.kernel.org/bpf/[email protected]/ Reported-by: Mykyta Yatsenko <[email protected]> Signed-off-by: Eduard Zingerman <[email protected]>
1 parent 13e07b5 commit 0cad325

File tree

1 file changed

+90
-3
lines changed

1 file changed

+90
-3
lines changed

tools/testing/selftests/bpf/unpriv_helpers.c

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,75 @@
11
// SPDX-License-Identifier: GPL-2.0-only
22

3+
#include <errno.h>
34
#include <stdbool.h>
45
#include <stdlib.h>
56
#include <stdio.h>
67
#include <string.h>
8+
#include <sys/utsname.h>
79
#include <unistd.h>
810
#include <fcntl.h>
11+
#include <zlib.h>
912

1013
#include "unpriv_helpers.h"
1114

12-
static bool get_mitigations_off(void)
15+
static gzFile open_config(void)
16+
{
17+
struct utsname uts;
18+
char buf[PATH_MAX];
19+
gzFile config;
20+
21+
if (uname(&uts)) {
22+
perror("uname");
23+
goto config_gz;
24+
}
25+
26+
snprintf(buf, sizeof(buf), "/boot/config-%s", uts.release);
27+
config = gzopen(buf, "rb");
28+
if (config)
29+
return config;
30+
fprintf(stderr, "gzopen %s: %s\n", buf, strerror(errno));
31+
32+
config_gz:
33+
config = gzopen("/proc/config.gz", "rb");
34+
if (!config)
35+
perror("gzopen /proc/config.gz");
36+
return config;
37+
}
38+
39+
static int config_contains(const char *pat)
40+
{
41+
const char *msg;
42+
char buf[1024];
43+
gzFile config;
44+
int n, err;
45+
46+
config = open_config();
47+
if (!config)
48+
return -1;
49+
50+
for (;;) {
51+
if (!gzgets(config, buf, sizeof(buf))) {
52+
msg = gzerror(config, &err);
53+
if (err == Z_ERRNO)
54+
perror("gzgets /proc/config.gz");
55+
else if (err != Z_OK)
56+
fprintf(stderr, "gzgets /proc/config.gz: %s", msg);
57+
gzclose(config);
58+
return -1;
59+
}
60+
n = strlen(buf);
61+
if (buf[n - 1] == '\n')
62+
buf[n - 1] = 0;
63+
if (strcmp(buf, pat) == 0) {
64+
gzclose(config);
65+
return 1;
66+
}
67+
}
68+
gzclose(config);
69+
return 0;
70+
}
71+
72+
static bool cmdline_contains(const char *pat)
1373
{
1474
char cmdline[4096], *c;
1575
int fd, ret = false;
@@ -27,7 +87,7 @@ static bool get_mitigations_off(void)
2787

2888
cmdline[sizeof(cmdline) - 1] = '\0';
2989
for (c = strtok(cmdline, " \n"); c; c = strtok(NULL, " \n")) {
30-
if (strncmp(c, "mitigations=off", strlen(c)))
90+
if (strncmp(c, pat, strlen(c)))
3191
continue;
3292
ret = true;
3393
break;
@@ -37,8 +97,21 @@ static bool get_mitigations_off(void)
3797
return ret;
3898
}
3999

100+
static int get_mitigations_off(void)
101+
{
102+
int enabled_in_config;
103+
104+
if (cmdline_contains("mitigations=off"))
105+
return 1;
106+
enabled_in_config = config_contains("CONFIG_CPU_MITIGATIONS=y");
107+
if (enabled_in_config < 0)
108+
return -1;
109+
return !enabled_in_config;
110+
}
111+
40112
bool get_unpriv_disabled(void)
41113
{
114+
int mitigations_off;
42115
bool disabled;
43116
char buf[2];
44117
FILE *fd;
@@ -52,5 +125,19 @@ bool get_unpriv_disabled(void)
52125
disabled = true;
53126
}
54127

55-
return disabled ? true : get_mitigations_off();
128+
if (disabled)
129+
return true;
130+
131+
/*
132+
* Some unpriv tests rely on spectre mitigations being on.
133+
* If mitigations are off or status can't be determined
134+
* assume that unpriv tests are disabled.
135+
*/
136+
mitigations_off = get_mitigations_off();
137+
if (mitigations_off < 0) {
138+
fprintf(stderr,
139+
"Can't determine if mitigations are enabled, disabling unpriv tests.");
140+
return true;
141+
}
142+
return mitigations_off;
56143
}

0 commit comments

Comments
 (0)