From: Artem Bityutskiy Date: Tue, 16 Dec 2025 08:04:02 +0000 (+0200) Subject: intel_idle: Add C-states validation X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=be6a150829b375c1b53d7ea5794ccc9edd2e0c9c;p=thirdparty%2Flinux.git intel_idle: Add C-states validation Add validation for C-states specified via the "table=" module parameter. Treat this module parameter as untrusted input and validate it thoroughly. Signed-off-by: Artem Bityutskiy Link: https://patch.msgid.link/20251216080402.156988-4-dedekind1@gmail.com Signed-off-by: Rafael J. Wysocki --- diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index ab6b86ff9905f..f49c939d636f4 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,11 @@ static bool ibrs_off __read_mostly; /* The maximum allowed length for the 'table' module parameter */ #define MAX_CMDLINE_TABLE_LEN 256 +/* Maximum allowed C-state latency */ +#define MAX_CMDLINE_LATENCY_US (5 * USEC_PER_MSEC) +/* Maximum allowed C-state target residency */ +#define MAX_CMDLINE_RESIDENCY_US (100 * USEC_PER_MSEC) + static char cmdline_table_str[MAX_CMDLINE_TABLE_LEN] __read_mostly; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; @@ -2434,6 +2440,41 @@ static char *get_cmdline_field(char *args, char **field, char sep) return args + i + 1; } +/** + * validate_cmdline_cstate - Validate a C-state from cmdline. + * @state: The C-state to validate. + * @prev_state: The previous C-state in the table or NULL. + * + * Return: 0 if the C-state is valid or -EINVAL otherwise. + */ +static int validate_cmdline_cstate(struct cpuidle_state *state, + struct cpuidle_state *prev_state) +{ + if (state->exit_latency == 0) + /* Exit latency 0 can only be used for the POLL state */ + return -EINVAL; + + if (state->exit_latency > MAX_CMDLINE_LATENCY_US) + return -EINVAL; + + if (state->target_residency > MAX_CMDLINE_RESIDENCY_US) + return -EINVAL; + + if (state->target_residency < state->exit_latency) + return -EINVAL; + + if (!prev_state) + return 0; + + if (state->exit_latency <= prev_state->exit_latency) + return -EINVAL; + + if (state->target_residency <= prev_state->target_residency) + return -EINVAL; + + return 0; +} + /** * cmdline_table_adjust - Adjust the C-states table with data from cmdline. * @drv: cpuidle driver (assumed to point to intel_idle_driver). @@ -2532,6 +2573,19 @@ static void __init cmdline_table_adjust(struct cpuidle_driver *drv) state->name, state->exit_latency, state->target_residency); } + /* Validate the adjusted C-states, start with index 1 to skip POLL */ + for (i = 1; i < drv->state_count; i++) { + struct cpuidle_state *prev_state; + + state = &cmdline_states[i]; + prev_state = &cmdline_states[i - 1]; + + if (validate_cmdline_cstate(state, prev_state)) { + pr_err("C-state '%s' validation failed\n", state->name); + goto error; + } + } + /* Copy the adjusted C-states table back */ for (i = 1; i < drv->state_count; i++) drv->states[i] = cmdline_states[i];