[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <lsq.1581185941.37930925@decadent.org.uk>
Date: Sat, 08 Feb 2020 18:21:07 +0000
From: Ben Hutchings <ben@...adent.org.uk>
To: linux-kernel@...r.kernel.org, stable@...r.kernel.org
CC: akpm@...ux-foundation.org, Denis Kirjanov <kda@...ux-powerpc.org>,
"Rafael J. Wysocki" <rafael.j.wysocki@...el.com>,
"Dmitry Safonov" <0x7f454c46@...il.com>,
"Francesco Ruggeri" <fruggeri@...sta.com>
Subject: [PATCH 3.16 128/148] ACPI: OSL: only free map once in osl.c
3.16.82-rc1 review patch. If anyone has any objections, please let me know.
------------------
From: Francesco Ruggeri <fruggeri@...sta.com>
commit 833a426cc471b6088011b3d67f1dc4e147614647 upstream.
acpi_os_map_cleanup checks map->refcount outside of acpi_ioremap_lock
before freeing the map. This creates a race condition the can result
in the map being freed more than once.
A panic can be caused by running
for ((i=0; i<10; i++))
do
for ((j=0; j<100000; j++))
do
cat /sys/firmware/acpi/tables/data/BERT >/dev/null
done &
done
This patch makes sure that only the process that drops the reference
to 0 does the freeing.
Fixes: b7c1fadd6c2e ("ACPI: Do not use krefs under a mutex in osl.c")
Signed-off-by: Francesco Ruggeri <fruggeri@...sta.com>
Reviewed-by: Dmitry Safonov <0x7f454c46@...il.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@...el.com>
Signed-off-by: Ben Hutchings <ben@...adent.org.uk>
---
drivers/acpi/osl.c | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -416,24 +416,27 @@ acpi_os_map_memory(acpi_physical_address
}
EXPORT_SYMBOL_GPL(acpi_os_map_memory);
-static void acpi_os_drop_map_ref(struct acpi_ioremap *map)
+/* Must be called with mutex_lock(&acpi_ioremap_lock) */
+static unsigned long acpi_os_drop_map_ref(struct acpi_ioremap *map)
{
- if (!--map->refcount)
+ unsigned long refcount = --map->refcount;
+
+ if (!refcount)
list_del_rcu(&map->list);
+ return refcount;
}
static void acpi_os_map_cleanup(struct acpi_ioremap *map)
{
- if (!map->refcount) {
- synchronize_rcu_expedited();
- acpi_unmap(map->phys, map->virt);
- kfree(map);
- }
+ synchronize_rcu_expedited();
+ acpi_unmap(map->phys, map->virt);
+ kfree(map);
}
void __ref acpi_os_unmap_iomem(void __iomem *virt, acpi_size size)
{
struct acpi_ioremap *map;
+ unsigned long refcount;
if (!acpi_gbl_permanent_mmap) {
__acpi_unmap_table(virt, size);
@@ -447,10 +450,11 @@ void __ref acpi_os_unmap_iomem(void __io
WARN(true, PREFIX "%s: bad address %p\n", __func__, virt);
return;
}
- acpi_os_drop_map_ref(map);
+ refcount = acpi_os_drop_map_ref(map);
mutex_unlock(&acpi_ioremap_lock);
- acpi_os_map_cleanup(map);
+ if (!refcount)
+ acpi_os_map_cleanup(map);
}
EXPORT_SYMBOL_GPL(acpi_os_unmap_iomem);
@@ -491,6 +495,7 @@ void acpi_os_unmap_generic_address(struc
{
u64 addr;
struct acpi_ioremap *map;
+ unsigned long refcount;
if (gas->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
return;
@@ -506,10 +511,11 @@ void acpi_os_unmap_generic_address(struc
mutex_unlock(&acpi_ioremap_lock);
return;
}
- acpi_os_drop_map_ref(map);
+ refcount = acpi_os_drop_map_ref(map);
mutex_unlock(&acpi_ioremap_lock);
- acpi_os_map_cleanup(map);
+ if (!refcount)
+ acpi_os_map_cleanup(map);
}
EXPORT_SYMBOL(acpi_os_unmap_generic_address);
Powered by blists - more mailing lists