| // SPDX-License-Identifier: GPL-2.0-only |
| |
| #include <errno.h> |
| #include <limits.h> |
| #include <stdbool.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/utsname.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <zlib.h> |
| |
| #include "unpriv_helpers.h" |
| |
| static gzFile open_config(void) |
| { |
| struct utsname uts; |
| char buf[PATH_MAX]; |
| gzFile config; |
| |
| if (uname(&uts)) { |
| perror("uname"); |
| goto config_gz; |
| } |
| |
| snprintf(buf, sizeof(buf), "/boot/config-%s", uts.release); |
| config = gzopen(buf, "rb"); |
| if (config) |
| return config; |
| fprintf(stderr, "gzopen %s: %s\n", buf, strerror(errno)); |
| |
| config_gz: |
| config = gzopen("/proc/config.gz", "rb"); |
| if (!config) |
| perror("gzopen /proc/config.gz"); |
| return config; |
| } |
| |
| static int config_contains(const char *pat) |
| { |
| const char *msg; |
| char buf[1024]; |
| gzFile config; |
| int n, err; |
| |
| config = open_config(); |
| if (!config) |
| return -1; |
| |
| for (;;) { |
| if (!gzgets(config, buf, sizeof(buf))) { |
| msg = gzerror(config, &err); |
| if (err == Z_ERRNO) |
| perror("gzgets /proc/config.gz"); |
| else if (err != Z_OK) |
| fprintf(stderr, "gzgets /proc/config.gz: %s", msg); |
| gzclose(config); |
| return -1; |
| } |
| n = strlen(buf); |
| if (buf[n - 1] == '\n') |
| buf[n - 1] = 0; |
| if (strcmp(buf, pat) == 0) { |
| gzclose(config); |
| return 1; |
| } |
| } |
| gzclose(config); |
| return 0; |
| } |
| |
| static bool cmdline_contains(const char *pat) |
| { |
| char cmdline[4096], *c; |
| int fd, ret = false; |
| |
| fd = open("/proc/cmdline", O_RDONLY); |
| if (fd < 0) { |
| perror("open /proc/cmdline"); |
| return false; |
| } |
| |
| if (read(fd, cmdline, sizeof(cmdline) - 1) < 0) { |
| perror("read /proc/cmdline"); |
| goto out; |
| } |
| |
| cmdline[sizeof(cmdline) - 1] = '\0'; |
| for (c = strtok(cmdline, " \n"); c; c = strtok(NULL, " \n")) { |
| if (strncmp(c, pat, strlen(c))) |
| continue; |
| ret = true; |
| break; |
| } |
| out: |
| close(fd); |
| return ret; |
| } |
| |
| static int get_mitigations_off(void) |
| { |
| int enabled_in_config; |
| |
| if (cmdline_contains("mitigations=off")) |
| return 1; |
| enabled_in_config = config_contains("CONFIG_CPU_MITIGATIONS=y"); |
| if (enabled_in_config < 0) |
| return -1; |
| return !enabled_in_config; |
| } |
| |
| bool get_unpriv_disabled(void) |
| { |
| int mitigations_off; |
| bool disabled; |
| char buf[2]; |
| FILE *fd; |
| |
| fd = fopen("/proc/sys/" UNPRIV_SYSCTL, "r"); |
| if (fd) { |
| disabled = (fgets(buf, 2, fd) == buf && atoi(buf)); |
| fclose(fd); |
| } else { |
| perror("fopen /proc/sys/" UNPRIV_SYSCTL); |
| disabled = true; |
| } |
| |
| if (disabled) |
| return true; |
| |
| /* |
| * Some unpriv tests rely on spectre mitigations being on. |
| * If mitigations are off or status can't be determined |
| * assume that unpriv tests are disabled. |
| */ |
| mitigations_off = get_mitigations_off(); |
| if (mitigations_off < 0) { |
| fprintf(stderr, |
| "Can't determine if mitigations are enabled, disabling unpriv tests."); |
| return true; |
| } |
| return mitigations_off; |
| } |