From 476c942d9b3efdfd2c7246225793d56568b42f40 Mon Sep 17 00:00:00 2001 From: Neal Liu Date: Thu, 23 Jul 2020 12:48:00 -0700 Subject: [PATCH] BACKPORT: FROMLIST: cpuidle: change enter_s2idle() prototype Control Flow Integrity(CFI) is a security mechanism that disallows changes to the original control flow graph of a compiled binary, making it significantly harder to perform such attacks. init_state_node() assign same function callback to different function pointer declarations. static int init_state_node(struct cpuidle_state *idle_state, const struct of_device_id *matches, struct device_node *state_node) { ... idle_state->enter = match_id->data; ... idle_state->enter_s2idle = match_id->data; } Function declarations: struct cpuidle_state { ... int (*enter) (struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); void (*enter_s2idle) (struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); }; In this case, either enter() or enter_s2idle() would cause CFI check failed since they use same callee. Align function prototype of enter() since it needs return value for some use cases. The return value of enter_s2idle() is no need currently. Bug: 159445192 Bug: 159672235 Change-Id: Iffc9e1b6a17471ca4291865ab3dc8803303a38ce Link: https://lore.kernel.org/lkml/1595820346-4361-2-git-send-email-neal.liu@mediatek.com/ Signed-off-by: Neal Liu Signed-off-by: Sami Tolvanen --- arch/arm/mach-tegra/cpuidle-tegra114.c | 4 +++- drivers/acpi/processor_idle.c | 10 ++++++---- drivers/idle/intel_idle.c | 6 ++++-- include/linux/cpuidle.h | 9 ++++++--- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-tegra/cpuidle-tegra114.c b/arch/arm/mach-tegra/cpuidle-tegra114.c index 5118f777fd66..0ccdcc89eeb9 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra114.c +++ b/arch/arm/mach-tegra/cpuidle-tegra114.c @@ -51,11 +51,13 @@ static int tegra114_idle_power_down(struct cpuidle_device *dev, return index; } -static void tegra114_idle_enter_s2idle(struct cpuidle_device *dev, +static int tegra114_idle_enter_s2idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { tegra114_idle_power_down(dev, drv, index); + + return 0; } #endif diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index ed56c6d20b08..53b6c3bb2f90 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -791,8 +791,8 @@ static int acpi_idle_enter(struct cpuidle_device *dev, return index; } -static void acpi_idle_enter_s2idle(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) +static int acpi_idle_enter_s2idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) { struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); @@ -800,16 +800,18 @@ static void acpi_idle_enter_s2idle(struct cpuidle_device *dev, struct acpi_processor *pr = __this_cpu_read(processors); if (unlikely(!pr)) - return; + return 0; if (pr->flags.bm_check) { acpi_idle_enter_bm(pr, cx, false); - return; + return 0; } else { ACPI_FLUSH_CPU_CACHE(); } } acpi_idle_do_entry(cx); + + return 0; } static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr, diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 347b08b56042..a5d6adc5b399 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -85,7 +85,7 @@ static const struct idle_cpu *icpu; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; static int intel_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); -static void intel_idle_s2idle(struct cpuidle_device *dev, +static int intel_idle_s2idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); static struct cpuidle_state *cpuidle_state_table; @@ -935,13 +935,15 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev, * @drv: cpuidle driver * @index: state index */ -static void intel_idle_s2idle(struct cpuidle_device *dev, +static int intel_idle_s2idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { unsigned long ecx = 1; /* break on interrupt flag */ unsigned long eax = flg2MWAIT(drv->states[index].flags); mwait_idle_with_hints(eax, ecx); + + return 0; } static void __setup_broadcast_timer(bool on) diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 4b6b5bea8f79..cc3a188ce82f 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -61,10 +61,13 @@ struct cpuidle_state { * CPUs execute ->enter_s2idle with the local tick or entire timekeeping * suspended, so it must not re-enable interrupts at any point (even * temporarily) or attempt to change states of clock event devices. + * + * This callback may point to the same function as ->enter if all of + * the above requirements are met by it. */ - void (*enter_s2idle) (struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index); + int (*enter_s2idle)(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index); }; /* Idle State Flags */