root/tools/testing/selftests/bpf/unpriv_helpers.c
// 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;
}