[PATCH v3 1/2] powerpc/eeh: Avoid use after free in eeh_handle_special_event()
Andrew Donnellan
andrew.donnellan at au1.ibm.com
Thu Apr 20 10:35:52 AEST 2017
On 19/04/17 17:39, Russell Currey wrote:
> eeh_handle_special_event() is called when an EEH event is detected but
> can't be narrowed down to a specific PE. This function looks through
> every PE to find one in an erroneous state, then calls the regular event
> handler eeh_handle_normal_event() once it knows which PE has an error.
>
> However, if eeh_handle_normal_event() found that the PE cannot possibly
> be recovered, it will free it, rendering the passed PE stale.
> This leads to a use after free in eeh_handle_special_event() as it attempts to
> clear the "recovering" state on the PE after eeh_handle_normal_event() returns.
>
> Thus, make sure the PE is valid when attempting to clear state in
> eeh_handle_special_event().
>
> Cc: <stable at vger.kernel.org> #3.10+
> Reported-by: Alexey Kardashevskiy <aik at ozlabs.ru>
> Signed-off-by: Russell Currey <ruscur at russell.cc>
Per our conversation about this yesterday:
Reviewed-by: Andrew Donnellan <andrew.donnellan at au1.ibm.com>
> ---
> V2: check a specific return path instead of looking at the PE itself
> V3: use a bool instead of a non-specific int return
> ---
> arch/powerpc/kernel/eeh_driver.c | 19 +++++++++++++++----
> 1 file changed, 15 insertions(+), 4 deletions(-)
>
> diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
> index b94887165a10..e50d1470714f 100644
> --- a/arch/powerpc/kernel/eeh_driver.c
> +++ b/arch/powerpc/kernel/eeh_driver.c
> @@ -724,7 +724,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus,
> */
> #define MAX_WAIT_FOR_RECOVERY 300
>
> -static void eeh_handle_normal_event(struct eeh_pe *pe)
> +static bool eeh_handle_normal_event(struct eeh_pe *pe)
> {
> struct pci_bus *frozen_bus;
> struct eeh_dev *edev, *tmp;
> @@ -736,7 +736,7 @@ static void eeh_handle_normal_event(struct eeh_pe *pe)
> if (!frozen_bus) {
> pr_err("%s: Cannot find PCI bus for PHB#%x-PE#%x\n",
> __func__, pe->phb->global_number, pe->addr);
> - return;
> + return false;
> }
>
> eeh_pe_update_time_stamp(pe);
> @@ -870,7 +870,7 @@ static void eeh_handle_normal_event(struct eeh_pe *pe)
> pr_info("EEH: Notify device driver to resume\n");
> eeh_pe_dev_traverse(pe, eeh_report_resume, NULL);
>
> - return;
> + return false;
>
> excess_failures:
> /*
> @@ -915,8 +915,12 @@ static void eeh_handle_normal_event(struct eeh_pe *pe)
> pci_lock_rescan_remove();
> pci_hp_remove_devices(frozen_bus);
> pci_unlock_rescan_remove();
> +
> + /* The passed PE should no longer be used */
> + return true;
> }
> }
> + return false;
> }
>
> static void eeh_handle_special_event(void)
> @@ -982,7 +986,14 @@ static void eeh_handle_special_event(void)
> */
> if (rc == EEH_NEXT_ERR_FROZEN_PE ||
> rc == EEH_NEXT_ERR_FENCED_PHB) {
> - eeh_handle_normal_event(pe);
> + /*
> + * eeh_handle_normal_event() can make the PE stale if it
> + * determines that the PE cannot possibly be recovered.
> + * Don't modify the PE state if that's the case.
> + */
> + if (eeh_handle_normal_event(pe))
> + continue;
> +
> eeh_pe_state_clear(pe, EEH_PE_RECOVERING);
> } else {
> pci_lock_rescan_remove();
>
--
Andrew Donnellan OzLabs, ADL Canberra
andrew.donnellan at au1.ibm.com IBM Australia Limited
More information about the Linuxppc-dev
mailing list