Access control / Privilege escalation via improper sysfs access checks

HIGH
torvalds/linux
Commit: 25ff5848c05b
Affected: v7.0-rc6 and earlier (pre-commit 25ff5848c05b..., i.e., 7.0-rc6 and older)
2026-04-26 19:55 UTC

Description

The commit enhances access control for SoC slider modparams used by x86_energy_perf_policy. Previously, the tool could read or write the slider_balance and slider_offset modparams (kernel sysfs entries under /sys/module/processor_thermal_soc_slider/) without verifying that the platform-profile was set to a compatible SoC profile (specifically a profile named "SoC Power Slider"). The patch adds gating logic to ensure these modparams are only accessible when the platform-profile is readable and the profile name matches POWER_SLIDER_NAME, and that the platform-profile and the slider modparams are accessible with the requested mode (read/write). This reduces the risk of unauthorized configuration changes via sysfs when the platform/driver is not in a state that supports or expects these adjustments (preventing potential privilege escalation or information disclosure through misconfigurations).

Proof of Concept

#!/usr/bin/env python3 """ Proof-of-concept: demonstrate the access-control gating before vs after the fix in a safe, isolated test environment (fake sysfs tree under a temporary dir). This is not exploit code for real systems, but shows how, prior to the fix, a write to the slider parameters could succeed regardless of the platform profile, whereas after the fix, the write would be blocked if the platform profile name is not the expected value ("SoC Power Slider"). """ import os import tempfile import shutil SOCPROFILE_BASE = "platform-profile-0" NAME_FILE = os.path.join("class", "platform-profile", SOCPROFILE_BASE, "name") PROFILE_FILE = os.path.join("class", "platform-profile", SOCPROFILE_BASE, "profile") SLIDER_BALANCE = os.path.join("module", "processor_thermal_soc_slider", "parameters", "slider_balance") SLIDER_OFFSET = os.path.join("module", "processor_thermal_soc_slider", "parameters", "slider_offset") POWER_SLIDER_NAME = "SoC Power Slider" # Pre-fix simulation: gating disabled (older behavior) def pre_fix_setup(tmp): base = os.path.join(tmp, "sys") os.makedirs(os.path.dirname(os.path.join(base, SLIDER_BALANCE)), exist_ok=True) os.makedirs(os.path.dirname(os.path.join(base, NAME_FILE)), exist_ok=True) # create slider files balance_path = os.path.join(base, SLIDER_BALANCE) offset_path = os.path.join(base, SLIDER_OFFSET) os.makedirs(os.path.dirname(balance_path), exist_ok=True) os.makedirs(os.path.dirname(offset_path), exist_ok=True) with open(balance_path, "w") as f: f.write("0") with open(offset_path, "w") as f: f.write("0") # create a non-matching platform-profile name name_path = os.path.join(base, NAME_FILE) prof_path = os.path.join(base, PROFILE_FILE) os.makedirs(os.path.dirname(name_path), exist_ok=True) with open(name_path, "w") as f: f.write("Other Profile") with open(prof_path, "w") as f: f.write("Other Profile") return base # Pre-fix write (no platform-profile check) def pre_fix_write(base, value): path = os.path.join(base, SLIDER_BALANCE) with open(path, "w") as f: f.write(str(value)) return path # Post-fix write: gating checks present (only allow if name == POWER_SLIDER_NAME) def post_fix_write(base, value): name_path = os.path.join(base, NAME_FILE) with open(name_path, "r") as f: name = f.read().strip() if name != POWER_SLIDER_NAME: raise PermissionError("Access denied: platform-profile-name mismatch: {}".format(name)) path = os.path.join(base, SLIDER_BALANCE) with open(path, "w") as f: f.write(str(value)) return path def main(): tmp = tempfile.mkdtemp() try: base = pre_fix_setup(tmp) # Pre-fix scenario: write should succeed regardless of platform-profile name print("Pre-fix: attempting write to slider_balance with non-matching platform-profile...") pre_fix_write(base, 100) print("Pre-fix: write succeeded (simulated).") # Change platform-profile-name to non-matching (simulate after fix state) # This is to demonstrate gating in the post-fix path with open(os.path.join(base, NAME_FILE), "w") as f: f.write("Other Profile") # Post-fix scenario: attempt to write with non-matching name should fail print("Post-fix: attempting write to slider_balance with non-matching platform-profile...") try: post_fix_write(base, 200) print("Post-fix: write unexpectedly succeeded (vulnerability would persist).") except PermissionError as e: print("Post-fix: blocked as expected:", e) finally: shutil.rmtree(tmp) if __name__ == "__main__": main()

Commit Details

Author: Len Brown

Date: 2026-04-15 19:12 UTC

Message:

tools/power x86_energy_perf_policy: Enhances SoC Slider related checks When processor_thermal_soc_slider is loaded, its slider and offset modparams are visible. Check that the driver actually registered the profile named "SoC Slider" before reading or writing these modparams. n.b. This utility allows writing the Slider and Offset modparams even if the driver policy is not "balanced". Currently the processor_thermal_soc_slider consults those modparams only in "balanced" mode. Signed-off-by: Len Brown <len.brown@intel.com>

Triage Assessment

Vulnerability Type: Access control / Privilege escalation

Confidence: HIGH

Reasoning:

The commit adds checks to ensure platform-profile and SoC slider parameters are only read/written when the driver policy is appropriate and the required files are accessible. This prevents reading or writing sensitive modparams (slider_balance/slider_offset) when the platform and driver are not in a valid state, reducing risk of unauthorized configuration changes via sysfs (potential privilege escalation or information disclosure).

Verification Assessment

Vulnerability Type: Access control / Privilege escalation via improper sysfs access checks

Confidence: HIGH

Affected Versions: v7.0-rc6 and earlier (pre-commit 25ff5848c05b..., i.e., 7.0-rc6 and older)

Code Diff

diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c index 1f330c82d7c138..83e5adbcda69e4 100644 --- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c @@ -104,9 +104,14 @@ char platform_profile[64]; #define PATH_SOC_SLIDER_BALANCE "/sys/module/processor_thermal_soc_slider/parameters/slider_balance" #define PATH_SOC_SLIDER_OFFSET "/sys/module/processor_thermal_soc_slider/parameters/slider_offset" #define PATH_PLATFORM_PROFILE "/sys/class/platform-profile/platform-profile-0/profile" +#define PATH_PLATFORM_PROFILE_NAME "/sys/class/platform-profile/platform-profile-0/name" +#define POWER_SLIDER_NAME "SoC Power Slider" static int use_android_msr_path; +static unsigned int read_sysfs(const char *, char *, size_t); +static int sysfs_read_string(const char *, char *, size_t); + /* * maintain compatibility with original implementation, but don't document it: */ @@ -551,39 +556,91 @@ void print_version(void) printf("x86_energy_perf_policy 2025.11.22 Len Brown <lenb@kernel.org>\n"); } +static int platform_profile_access(int mode) +{ + if (access(PATH_PLATFORM_PROFILE, mode)) { + if (debug) + fprintf(stderr, "Can not access %s\n", PATH_PLATFORM_PROFILE); + return 0; + } + + return 1; +} + +static int platform_profile_name_is(char *name) +{ + char buf[64]; + + if (sysfs_read_string(PATH_PLATFORM_PROFILE_NAME, buf, sizeof(buf)) != 0) { + if (debug) + fprintf(stderr, "Can not read %s\n", PATH_PLATFORM_PROFILE_NAME); + return 0; + } + + if (strncmp(buf, name, 16)) { + if (debug) + fprintf(stderr, "%s does not match '%s'\n", PATH_PLATFORM_PROFILE_NAME, name); + return 0; + } + + return 1; +} + +static int soc_slider_access(int mode) +{ + if (!platform_profile_access(R_OK)) + return 0; + + if (!platform_profile_name_is(POWER_SLIDER_NAME)) + return 0; + + if (access(PATH_SOC_SLIDER_BALANCE, mode)) { + if (debug) + fprintf(stderr, "Can not access %s\n", PATH_SOC_SLIDER_BALANCE); + return 0; + } + + if (access(PATH_SOC_SLIDER_OFFSET, mode)) { + if (debug) + fprintf(stderr, "Can not access %s\n", PATH_SOC_SLIDER_OFFSET); + return 0; + } + + return 1; +} + void cmdline(int argc, char **argv) { int opt; int option_index = 0; static struct option long_options[] = { - {"all", required_argument, 0, 'a'}, - {"cpu", required_argument, 0, 'c'}, - {"pkg", required_argument, 0, 'p'}, - {"debug", no_argument, 0, 'd'}, - {"hwp-desired", required_argument, 0, 'D'}, - {"epb", required_argument, 0, 'B'}, - {"force", no_argument, 0, 'f'}, - {"hwp-enable", no_argument, 0, 'e'}, - {"help", no_argument, 0, 'h'}, - {"hwp-epp", required_argument, 0, 'P'}, - {"hwp-min", required_argument, 0, 'm'}, - {"hwp-max", required_argument, 0, 'M'}, - {"read", no_argument, 0, 'r'}, - {"turbo-enable", required_argument, 0, 't'}, - {"hwp-use-pkg", required_argument, 0, 'u'}, - {"version", no_argument, 0, 'v'}, - {"hwp-window", required_argument, 0, 'w'}, - {"soc-slider-balance", required_argument, 0, 'S'}, - {"soc-slider-offset", required_argument, 0, 'O'}, - {"platform-profile", required_argument, 0, 'F'}, - {0, 0, 0, 0 } + { "all", required_argument, 0, 'a' }, + { "cpu", required_argument, 0, 'c' }, + { "pkg", required_argument, 0, 'p' }, + { "debug", no_argument, 0, 'd' }, + { "hwp-desired", required_argument, 0, 'D' }, + { "epb", required_argument, 0, 'B' }, + { "force", no_argument, 0, 'f' }, + { "hwp-enable", no_argument, 0, 'e' }, + { "help", no_argument, 0, 'h' }, + { "hwp-epp", required_argument, 0, 'P' }, + { "hwp-min", required_argument, 0, 'm' }, + { "hwp-max", required_argument, 0, 'M' }, + { "read", no_argument, 0, 'r' }, + { "turbo-enable", required_argument, 0, 't' }, + { "hwp-use-pkg", required_argument, 0, 'u' }, + { "version", no_argument, 0, 'v' }, + { "hwp-window", required_argument, 0, 'w' }, + { "soc-slider-balance", required_argument, 0, 'S' }, + { "soc-slider-offset", required_argument, 0, 'O' }, + { "platform-profile", required_argument, 0, 'F' }, + { 0, 0, 0, 0 } }; progname = argv[0]; - while ((opt = getopt_long_only(argc, argv, "+a:c:dD:E:e:f:m:M:rt:u:vw::S:O:F:", - long_options, &option_index)) != -1) { + while ((opt = getopt_long_only(argc, argv, "+a:c:dD:E:e:f:m:M:rt:u:vw::S:O:F:", long_options, &option_index)) != -1) { switch (opt) { case 'a': parse_cmdline_all(optarg); @@ -613,6 +670,8 @@ void cmdline(int argc, char **argv) case 'F': if (strlen(optarg) >= sizeof(platform_profile)) errx(1, "--platform-profile: value too long"); + if (!platform_profile_access(W_OK)) + errx(1, "Can not update platform-profile in '%s'", PATH_PLATFORM_PROFILE); strcpy(platform_profile, optarg); update_platform_profile = 1; break; @@ -625,6 +684,8 @@ void cmdline(int argc, char **argv) case 'O': if (parse_cmdline_int(optarg, &soc_slider_offset)) errx(1, "--soc-slider-offset: invalid value"); + if (!soc_slider_access(W_OK)) + errx(1, "Unable to write SOC Slider Offset"); update_soc_slider_offset = 1; break; case 'p': @@ -639,6 +700,8 @@ void cmdline(int argc, char **argv) case 'S': if (parse_cmdline_int(optarg, &soc_slider_balance)) errx(1, "--soc-slider-balance: invalid value"); + if (!soc_slider_access(W_OK)) + errx(1, "Unable to write SOC Slider-Balance in '%s'", PATH_SOC_SLIDER_BALANCE); update_soc_slider_balance = 1; break; case 't': @@ -814,7 +877,8 @@ static unsigned int write_sysfs(const char *path, char *buf, size_t buflen) numwritten = write(fd, buf, buflen - 1); if (numwritten < 1) { - perror("write failed\n"); + buf[strcspn(buf, "\n")] = '\0'; + warn("Write '%s' to '%s' failed", buf, path); close(fd); return -1; } @@ -972,27 +1036,30 @@ static int set_epb_sysfs(int cpu, int val) return (int)val; } -static int soc_slider_available(void) -{ - if (access(PATH_SOC_SLIDER_BALANCE, R_OK) && - access(PATH_SOC_SLIDER_OFFSET, R_OK) && - access(PATH_PLATFORM_PROFILE, R_OK)) - return 0; - - return 1; -} - static void print_soc_slider(void) { char buf[64]; - if (!soc_slider_available()) + if (!soc_slider_access(R_OK)) return; if (sysfs_read_string(PATH_SOC_SLIDER_BALANCE, buf, sizeof(buf)) == 0) printf("soc-slider-balance: %s\n", buf); + if (sysfs_read_string(PATH_SOC_SLIDER_OFFSET, buf, sizeof(buf)) == 0) printf("soc-slider-offset: %s\n", buf); +} + +static void print_platform_profile(void) +{ + char buf[64]; + + if (!platform_profile_access(R_OK)) + return; + + if (sysfs_read_string(PATH_PLATFORM_PROFILE_NAME, buf, sizeof(buf)) == 0) + printf("platform-profile-name: %s\n", buf); + if (sysfs_read_string(PATH_PLATFORM_PROFILE, buf, sizeof(buf)) == 0) printf("platform-profile: %s\n", buf); } @@ -1725,13 +1792,12 @@ int main(int argc, char **argv) return -EINVAL; /* display information only, no updates to settings */ - if (!update_epb && !update_turbo && !hwp_update_enabled() && - !update_soc_slider_balance && !update_soc_slider_offset && - !update_platform_profile) { + if (!update_epb && !update_turbo && !hwp_update_enabled() && !update_soc_slider_balance && !update_soc_slider_offset && !update_platform_profile) { if (cpu_selected_set) for_all_cpus_in_set(cpu_setsize, cpu_selected_set, print_cpu_msrs); print_soc_slider(); + print_platform_profile(); if (has_hwp_request_pkg) { if (pkg_selected_set == 0)
← Back to Alerts View on GitHub →