* [PATCH 0/3] ACPI / Battery: fix NULL pointer dereference from battery
@ 2011-07-12 8:03 Stefan Hajnoczi
2011-07-12 8:03 ` [PATCH 1/3] power_supply: scrub device pointer if registration fails Stefan Hajnoczi
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Stefan Hajnoczi @ 2011-07-12 8:03 UTC (permalink / raw)
To: Len Brown, Anton Vorontsov, David Woodhouse
Cc: linux-acpi, linux-kernel, Stefan Hajnoczi
The following oops happens non-deterministically when resuming from suspend on
a recent Acer Aspire laptop. The issue seems to be unregistering a led trigger
that was never registered. The led_trigger->next_trig list_head fields contain
zeroes, hence the NULL pointer dereference in list_del().
Due to the non-deterministic nature of the bug I am not sure whether the
patches eliminate the oops. However, the patches do address real problems in
drivers/acpi/battery.c.
There is a lack of error handling in battery.c so when power_supply_register()
fails the power_supply will continue be used as if there was no error. The
reason for this is that power_supply_register() leaves a stale
power_supply->dev pointer set when it returns an error and battery.c simply
uses that field to decide whether the power_supply is registered or not. This
is fixed in the first patch.
The second and third patches clean up the error handling in acpi_battery_add()
to prevent a use-after-free and properly release resources.
Here is the oops from a Debian 2.6.39-2-amd64 kernel. linux-2.6/master
contains no relevant fixes AFAICT. Any thoughts on what is going on here
appreciated:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 IP: [<ffffffff81263f1a>] led_trigger_unregister+0x36/0xb4
PGD 1b3d13067 PUD 118545067 PMD 0
Oops: 0002 [#1] SMP
last sysfs file: /sys/devices/virtual/vc/vcsa63/uevent
CPU 0
Modules linked in: parport_pc ppdev lp parport rfcomm bnep bluetooth crc16 acpi_cpufreq mperf cpufreq_conservative cpufreq_stats cpufreq_userspace cpufreq_powersave uinput fuse ext2 loop snd_hda_codec_hdmi snd_hda_codec_realtek joydev arc4 ecb ath9k snd_hda_intel mac80211 snd_hda_codec snd_hwdep snd_pcm ath9k_common ath9k_hw snd_seq snd_timer i915 ath snd_seq_device snd uvcvideo videodev drm_kms_helper soundcore cfg80211 drm snd_page_alloc ac sparse_keymap media wmi battery rfkill v4l2_compat_ioctl32 intel_ips pcspkr evdev i2c_i801 power_supply i2c_algo_bit i2c_core psmouse video processor button serio_raw ext3 jbd mbcache sha256_generic cryptd aes_x86_64 aes_generic cbc dm_crypt dm_mod sd_mod crc_t10dif ahci libahci libata scsi_mod ehci_hcd usbcore atl1c thermal thermal_sys [last unloaded
: scsi_wait_scan]
Pid: 29129, comm: pm-suspend Not tainted 2.6.39-2-amd64 #1 Acer Aspire 3820/JM31_CP
RIP: 0010:[<ffffffff81263f1a>] [<ffffffff81263f1a>] led_trigger_unregister+0x36/0xb4
RSP: 0018:ffff8801187fbd68 EFLAGS: 00010246
RAX: 0000000000000000 RBX: dead000000200200 RCX: 0000000000000004
RDX: 0000000000000000 RSI: dead000000100100 RDI: ffffffff8164ede0
^ ^
rdx gets dereferenced LIST_POISON constants (haven't been used yet)
RBP: ffff8801b321aec0 R08: 0000000000000200 R09: ffffffff81683390
R10: ffff880100000010 R11: ffff8801b0e3f800 R12: 00000000ffffffff
R13: ffff8801b3d93378 R14: 0000000000000003 R15: ffffffffffffffff
FS: 00007f83b6797700(0000) GS:ffff8801bbc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
CR2: 0000000000000008 CR3: 00000001b21cc000 CR4: 00000000000006f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process pm-suspend (pid: 29129, threadinfo ffff8801187fa000, task ffff8801b2880e40)
Stack:
ffff8801b321aec0 0000000000000004 00000000ffffffff ffffffff81263fa6
ffff8801b0e3f820 ffffffffa01716b2 ffff8801b0e3f820 ffffffffa0171035
ffff8801b0e3f800 ffffffffa018628f ffff8801b0e3f800 ffffffffa01863ae
Call Trace:
[<ffffffff81263fa6>] ? led_trigger_unregister_simple+0xe/0x17
[<ffffffffa01716b2>] ? power_supply_remove_triggers+0x16/0x80 [power_supply]
[<ffffffffa0171035>] ? power_supply_unregister+0x15/0x1f [power_supply]
[<ffffffffa018628f>] ? sysfs_remove_battery+0x25/0x32 [battery]
[<ffffffffa01863ae>] ? battery_notify+0x16/0x22 [battery]
[<ffffffff81335b48>] ? notifier_call_chain+0x2e/0x5b
[<ffffffff810634ab>] ? __blocking_notifier_call_chain+0x4c/0x63
[<ffffffff81076439>] ? pm_notifier_call_chain+0x15/0x2a
[<ffffffff81076ca0>] ? enter_state+0x10c/0x12b
[<ffffffff8107638e>] ? state_store+0xb1/0xce
[<ffffffff8114ed16>] ? sysfs_write_file+0xe0/0x11c
[<ffffffff810fbfe8>] ? vfs_write+0xa4/0xff
[<ffffffff810fc0f9>] ? sys_write+0x45/0x6e
[<ffffffff81338dd2>] ? system_call_fastpath+0x16/0x1b
Code: 64 81 53 48 bb 00 02 20 00 00 00 ad de e8 fc e2 0c 00 48 8b 55 30 48 8b 45 38 48 be 00 01 10 00 00 00 ad de 48 c7 c7 e0 ed 64 81
89 42 08 48 89 10 48 89 75 30 48 89 5d 38 e8 60 ec df ff 48
RIP [<ffffffff81263f1a>] led_trigger_unregister+0x36/0xb4
RSP <ffff8801187fbd68>
CR2: 0000000000000008
---[ end trace 099c095de50533f3 ]---
Disassembly of led_trigger_unregister:
ffffffff81263ee4 <led_trigger_unregister>:
ffffffff81263ee4: 41 54 push %r12
ffffffff81263ee6: 55 push %rbp
ffffffff81263ee7: 48 89 fd mov %rdi,%rbp
ffffffff81263eea: 48 c7 c7 e0 ed 64 81 mov $0xffffffff8164ede0,%rdi
ffffffff81263ef1: 53 push %rbx
ffffffff81263ef2: 48 bb 00 02 20 00 00 movabs $0xdead000000200200,%rbx
ffffffff81263ef9: 00 ad de
ffffffff81263efc: e8 fc e2 0c 00 callq ffffffff813321fd <down_write>
ffffffff81263f01: 48 8b 55 30 mov 0x30(%rbp),%rdx
ffffffff81263f05: 48 8b 45 38 mov 0x38(%rbp),%rax
ffffffff81263f09: 48 be 00 01 10 00 00 movabs $0xdead000000100100,%rsi
ffffffff81263f10: 00 ad de
ffffffff81263f13: 48 c7 c7 e0 ed 64 81 mov $0xffffffff8164ede0,%rdi
ffffffff81263f1a: 48 89 42 08 mov %rax,0x8(%rdx)
^--- boom!
Stefan Hajnoczi (3):
power_supply: scrub device pointer if registration fails
ACPI / Battery: avoid acpi_battery_add() use-after-free
ACPI / Battery: propagate sysfs error in acpi_battery_add()
drivers/acpi/battery.c | 29 ++++++++++++++++++++---------
drivers/power/power_supply_core.c | 1 +
2 files changed, 21 insertions(+), 9 deletions(-)
--
1.7.5.4
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/3] power_supply: scrub device pointer if registration fails
2011-07-12 8:03 [PATCH 0/3] ACPI / Battery: fix NULL pointer dereference from battery Stefan Hajnoczi
@ 2011-07-12 8:03 ` Stefan Hajnoczi
2011-07-12 15:09 ` Anton Vorontsov
2011-07-12 8:03 ` [PATCH 2/3] ACPI / Battery: avoid acpi_battery_add() use-after-free Stefan Hajnoczi
2011-07-12 8:03 ` [PATCH 3/3] ACPI / Battery: propagate sysfs error in acpi_battery_add() Stefan Hajnoczi
2 siblings, 1 reply; 10+ messages in thread
From: Stefan Hajnoczi @ 2011-07-12 8:03 UTC (permalink / raw)
To: Len Brown, Anton Vorontsov, David Woodhouse
Cc: linux-acpi, linux-kernel, Stefan Hajnoczi
This patch makes power_supply_register() safer for callers that are not
being careful. When the function fails it leaves the caller's psy.dev
pointer set to the stale power supply device. A correct caller would
handle the error return and never use psy.dev but the example of
drivers/acpi/battery.c shows otherwise.
Clear the psy.dev pointer when power_supply_register() fails so the
caller either sees a valid pointer on success or NULL on failure.
Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
---
drivers/power/power_supply_core.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 329b46b..33d4068 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -194,6 +194,7 @@ create_triggers_failed:
kobject_set_name_failed:
device_add_failed:
put_device(dev);
+ psy->dev = NULL; /* make it crystal-clear that we failed */
success:
return rc;
}
--
1.7.5.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/3] ACPI / Battery: avoid acpi_battery_add() use-after-free
2011-07-12 8:03 [PATCH 0/3] ACPI / Battery: fix NULL pointer dereference from battery Stefan Hajnoczi
2011-07-12 8:03 ` [PATCH 1/3] power_supply: scrub device pointer if registration fails Stefan Hajnoczi
@ 2011-07-12 8:03 ` Stefan Hajnoczi
2011-07-16 22:56 ` Len Brown
2011-07-12 8:03 ` [PATCH 3/3] ACPI / Battery: propagate sysfs error in acpi_battery_add() Stefan Hajnoczi
2 siblings, 1 reply; 10+ messages in thread
From: Stefan Hajnoczi @ 2011-07-12 8:03 UTC (permalink / raw)
To: Len Brown, Anton Vorontsov, David Woodhouse
Cc: linux-acpi, linux-kernel, Stefan Hajnoczi
When acpi_battery_add_fs() fails the error handling code does not clean
up completely. Moreover, it does not return resulting in a
use-after-free.
Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
---
drivers/acpi/battery.c | 18 ++++++++++++------
1 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index fcc13ac..6b3aeba 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -979,21 +979,27 @@ static int acpi_battery_add(struct acpi_device *device)
#ifdef CONFIG_ACPI_PROCFS_POWER
result = acpi_battery_add_fs(device);
#endif
- if (!result) {
- printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
- ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
- device->status.battery_present ? "present" : "absent");
- } else {
+ if (result) {
#ifdef CONFIG_ACPI_PROCFS_POWER
acpi_battery_remove_fs(device);
#endif
- kfree(battery);
+ goto fail;
}
+ printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
+ ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
+ device->status.battery_present ? "present" : "absent");
+
battery->pm_nb.notifier_call = battery_notify;
register_pm_notifier(&battery->pm_nb);
return result;
+
+fail:
+ sysfs_remove_battery(battery);
+ mutex_destroy(&battery->lock);
+ kfree(battery);
+ return result;
}
static int acpi_battery_remove(struct acpi_device *device, int type)
--
1.7.5.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/3] ACPI / Battery: propagate sysfs error in acpi_battery_add()
2011-07-12 8:03 [PATCH 0/3] ACPI / Battery: fix NULL pointer dereference from battery Stefan Hajnoczi
2011-07-12 8:03 ` [PATCH 1/3] power_supply: scrub device pointer if registration fails Stefan Hajnoczi
2011-07-12 8:03 ` [PATCH 2/3] ACPI / Battery: avoid acpi_battery_add() use-after-free Stefan Hajnoczi
@ 2011-07-12 8:03 ` Stefan Hajnoczi
2011-07-16 22:58 ` Len Brown
2 siblings, 1 reply; 10+ messages in thread
From: Stefan Hajnoczi @ 2011-07-12 8:03 UTC (permalink / raw)
To: Len Brown, Anton Vorontsov, David Woodhouse
Cc: linux-acpi, linux-kernel, Stefan Hajnoczi
Make sure the error return from sysfs_add_battery() is checked and
propagated out from acpi_battery_add().
Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
---
drivers/acpi/battery.c | 11 ++++++++---
1 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 6b3aeba..2ae2fca 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -626,8 +626,11 @@ static int acpi_battery_update(struct acpi_battery *battery)
acpi_battery_quirks(battery);
acpi_battery_init_alarm(battery);
}
- if (!battery->bat.dev)
- sysfs_add_battery(battery);
+ if (!battery->bat.dev) {
+ result = sysfs_add_battery(battery);
+ if (result)
+ return result;
+ }
result = acpi_battery_get_state(battery);
acpi_battery_quirks2(battery);
return result;
@@ -975,7 +978,9 @@ static int acpi_battery_add(struct acpi_device *device)
if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle,
"_BIX", &handle)))
set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
- acpi_battery_update(battery);
+ result = acpi_battery_update(battery);
+ if (result)
+ goto fail;
#ifdef CONFIG_ACPI_PROCFS_POWER
result = acpi_battery_add_fs(device);
#endif
--
1.7.5.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] power_supply: scrub device pointer if registration fails
2011-07-12 8:03 ` [PATCH 1/3] power_supply: scrub device pointer if registration fails Stefan Hajnoczi
@ 2011-07-12 15:09 ` Anton Vorontsov
2011-07-12 15:30 ` Stefan Hajnoczi
0 siblings, 1 reply; 10+ messages in thread
From: Anton Vorontsov @ 2011-07-12 15:09 UTC (permalink / raw)
To: Stefan Hajnoczi; +Cc: Len Brown, David Woodhouse, linux-acpi, linux-kernel
On Tue, Jul 12, 2011 at 09:03:27AM +0100, Stefan Hajnoczi wrote:
> This patch makes power_supply_register() safer for callers that are not
> being careful. When the function fails it leaves the caller's psy.dev
> pointer set to the stale power supply device. A correct caller would
> handle the error return and never use psy.dev but the example of
> drivers/acpi/battery.c shows otherwise.
>
> Clear the psy.dev pointer when power_supply_register() fails so the
> caller either sees a valid pointer on success or NULL on failure.
>
> Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
> ---
> drivers/power/power_supply_core.c | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
> index 329b46b..33d4068 100644
> --- a/drivers/power/power_supply_core.c
> +++ b/drivers/power/power_supply_core.c
> @@ -194,6 +194,7 @@ create_triggers_failed:
> kobject_set_name_failed:
> device_add_failed:
> put_device(dev);
> + psy->dev = NULL; /* make it crystal-clear that we failed */
> success:
> return rc;
> }
I think this may easily cause races. I.e.
- ACPI calls power_supply_register, it allocates dev, sets
psy->dev;
- Someone calls acpi_battery_notify() or acpi_battery_update(),
which tests for psy->dev;
- power_supply_register fails, it frees dev, and then clears psy->dev;
but it's too late, as acpi_battery_notify/acpi_battery_update thinks
that we're fine.
I believe the whole ACPI battery logic is overcomplicated, and
needs a bit of rework. In the meantime, we could move 'psy->dev =
dev;' assignment into the end of the function, where _register
could not fail, i.e. something like this:
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 329b46b..9f85b70 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -169,7 +169,6 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
dev->parent = parent;
dev->release = power_supply_dev_release;
dev_set_drvdata(dev, psy);
- psy->dev = dev;
INIT_WORK(&psy->changed_work, power_supply_changed_work);
@@ -185,6 +184,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
if (rc)
goto create_triggers_failed;
+ psy->dev = dev;
+
power_supply_changed(psy);
goto success;
But still, I don't see how this will save us from the same issue
when ACPI calls power_supply_unregister, which doesn't clear psy->dev:
static void acpi_battery_refresh(struct acpi_battery *battery)
{
if (!battery->bat.dev)
return;
acpi_battery_get_info(battery);
/* The battery may have changed its reporting units. */
sysfs_remove_battery(battery);
sysfs_add_battery(battery);
}
Really, ACPI battery needs some proper fixing and locking. :-/
--
Anton Vorontsov
Email: cbouatmailru@gmail.com
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] power_supply: scrub device pointer if registration fails
2011-07-12 15:09 ` Anton Vorontsov
@ 2011-07-12 15:30 ` Stefan Hajnoczi
2011-07-12 17:23 ` Anton Vorontsov
0 siblings, 1 reply; 10+ messages in thread
From: Stefan Hajnoczi @ 2011-07-12 15:30 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: Len Brown, David Woodhouse, linux-acpi, linux-kernel
On Tue, Jul 12, 2011 at 07:09:42PM +0400, Anton Vorontsov wrote:
> On Tue, Jul 12, 2011 at 09:03:27AM +0100, Stefan Hajnoczi wrote:
> > This patch makes power_supply_register() safer for callers that are not
> > being careful. When the function fails it leaves the caller's psy.dev
> > pointer set to the stale power supply device. A correct caller would
> > handle the error return and never use psy.dev but the example of
> > drivers/acpi/battery.c shows otherwise.
> >
> > Clear the psy.dev pointer when power_supply_register() fails so the
> > caller either sees a valid pointer on success or NULL on failure.
> >
> > Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
> > ---
> > drivers/power/power_supply_core.c | 1 +
> > 1 files changed, 1 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
> > index 329b46b..33d4068 100644
> > --- a/drivers/power/power_supply_core.c
> > +++ b/drivers/power/power_supply_core.c
> > @@ -194,6 +194,7 @@ create_triggers_failed:
> > kobject_set_name_failed:
> > device_add_failed:
> > put_device(dev);
> > + psy->dev = NULL; /* make it crystal-clear that we failed */
> > success:
> > return rc;
> > }
>
> I think this may easily cause races. I.e.
>
> - ACPI calls power_supply_register, it allocates dev, sets
> psy->dev;
> - Someone calls acpi_battery_notify() or acpi_battery_update(),
> which tests for psy->dev;
> - power_supply_register fails, it frees dev, and then clears psy->dev;
> but it's too late, as acpi_battery_notify/acpi_battery_update thinks
> that we're fine.
>
> I believe the whole ACPI battery logic is overcomplicated, and
> needs a bit of rework. In the meantime, we could move 'psy->dev =
> dev;' assignment into the end of the function, where _register
> could not fail, i.e. something like this:
Aha! I didn't do this is because I don't know the code and was afraid
some other function somewhere would use psy->dev. If you think it is
safer this way I'll resend the patch.
> But still, I don't see how this will save us from the same issue
> when ACPI calls power_supply_unregister, which doesn't clear psy->dev:
>
> static void acpi_battery_refresh(struct acpi_battery *battery)
> {
> if (!battery->bat.dev)
> return;
>
> acpi_battery_get_info(battery);
> /* The battery may have changed its reporting units. */
> sysfs_remove_battery(battery);
> sysfs_add_battery(battery);
> }
>
> Really, ACPI battery needs some proper fixing and locking. :-/
Yeah.
Stefan
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] power_supply: scrub device pointer if registration fails
2011-07-12 15:30 ` Stefan Hajnoczi
@ 2011-07-12 17:23 ` Anton Vorontsov
2011-07-14 6:07 ` Stefan Hajnoczi
0 siblings, 1 reply; 10+ messages in thread
From: Anton Vorontsov @ 2011-07-12 17:23 UTC (permalink / raw)
To: Stefan Hajnoczi; +Cc: Len Brown, David Woodhouse, linux-acpi, linux-kernel
On Tue, Jul 12, 2011 at 04:30:17PM +0100, Stefan Hajnoczi wrote:
[...]
> > I believe the whole ACPI battery logic is overcomplicated, and
> > needs a bit of rework. In the meantime, we could move 'psy->dev =
> > dev;' assignment into the end of the function, where _register
> > could not fail, i.e. something like this:
>
> Aha! I didn't do this is because I don't know the code and was afraid
> some other function somewhere would use psy->dev. If you think it is
> safer this way I'll resend the patch.
Neither is a proper fix, unfortunately. :-( Without some external lock
you can't use psy->dev as a flag to check if the registration was
successful. There is really no point trying to force core functions
to keep psy->dev in a sane state after registration has failed.
So, instead of patching power_supply core, I'd suggest the
following patch (on top of 2/3 and 3/3, so far only compile-
tested). How does it look?
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 2ae2fca..475e17c 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -101,6 +101,7 @@ enum {
struct acpi_battery {
struct mutex lock;
+ struct mutex bat_lock;
struct power_supply bat;
struct acpi_device *device;
struct notifier_block pm_nb;
@@ -559,8 +560,10 @@ static int sysfs_add_battery(struct acpi_battery *battery)
battery->bat.get_property = acpi_battery_get_property;
result = power_supply_register(&battery->device->dev, &battery->bat);
- if (result)
+ if (result) {
+ battery->bat.dev = NULL;
return result;
+ }
return device_create_file(battery->bat.dev, &alarm_attr);
}
@@ -613,31 +616,40 @@ static int acpi_battery_update(struct acpi_battery *battery)
result = acpi_battery_get_status(battery);
if (result)
return result;
+
+ mutex_lock(&battery->bat_lock);
+
if (!acpi_battery_present(battery)) {
sysfs_remove_battery(battery);
battery->update_time = 0;
- return 0;
+ result = 0;
+ goto out_unlock;
}
if (!battery->update_time ||
old_present != acpi_battery_present(battery)) {
result = acpi_battery_get_info(battery);
if (result)
- return result;
+ goto out_unlock;
acpi_battery_quirks(battery);
acpi_battery_init_alarm(battery);
}
if (!battery->bat.dev) {
result = sysfs_add_battery(battery);
if (result)
- return result;
+ goto out_unlock;
}
result = acpi_battery_get_state(battery);
acpi_battery_quirks2(battery);
+
+out_unlock:
+ mutex_unlock(&battery->bat_lock);
return result;
}
-static void acpi_battery_refresh(struct acpi_battery *battery)
+static void sysfs_readd_battery(struct acpi_battery *battery)
{
+ mutex_lock(&battery->bat_lock);
+
if (!battery->bat.dev)
return;
@@ -645,6 +657,13 @@ static void acpi_battery_refresh(struct acpi_battery *battery)
/* The battery may have changed its reporting units. */
sysfs_remove_battery(battery);
sysfs_add_battery(battery);
+
+ mutex_unlock(&battery->bat_lock);
+}
+
+static void acpi_battery_refresh(struct acpi_battery *battery)
+{
+ sysfs_readd_battery(battery);
}
/* --------------------------------------------------------------------------
@@ -941,8 +960,10 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event)
dev_name(&device->dev), event,
acpi_battery_present(battery));
/* acpi_battery_update could remove power_supply object */
+ mutex_lock(&battery->bat_lock);
if (old && battery->bat.dev)
power_supply_changed(&battery->bat);
+ mutex_unlock(&battery->bat_lock);
}
static int battery_notify(struct notifier_block *nb,
@@ -952,8 +973,7 @@ static int battery_notify(struct notifier_block *nb,
pm_nb);
switch (mode) {
case PM_POST_SUSPEND:
- sysfs_remove_battery(battery);
- sysfs_add_battery(battery);
+ sysfs_readd_battery(battery);
break;
}
@@ -975,6 +995,7 @@ static int acpi_battery_add(struct acpi_device *device)
strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
device->driver_data = battery;
mutex_init(&battery->lock);
+ mutex_init(&battery->bat_lock);
if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle,
"_BIX", &handle)))
set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
@@ -1019,6 +1040,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type)
acpi_battery_remove_fs(device);
#endif
sysfs_remove_battery(battery);
+ mutex_destroy(&battery->bat_lock);
mutex_destroy(&battery->lock);
kfree(battery);
return 0;
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] power_supply: scrub device pointer if registration fails
2011-07-12 17:23 ` Anton Vorontsov
@ 2011-07-14 6:07 ` Stefan Hajnoczi
0 siblings, 0 replies; 10+ messages in thread
From: Stefan Hajnoczi @ 2011-07-14 6:07 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: Len Brown, David Woodhouse, linux-acpi, linux-kernel
On Tue, Jul 12, 2011 at 09:23:31PM +0400, Anton Vorontsov wrote:
> On Tue, Jul 12, 2011 at 04:30:17PM +0100, Stefan Hajnoczi wrote:
> [...]
> > > I believe the whole ACPI battery logic is overcomplicated, and
> > > needs a bit of rework. In the meantime, we could move 'psy->dev =
> > > dev;' assignment into the end of the function, where _register
> > > could not fail, i.e. something like this:
> >
> > Aha! I didn't do this is because I don't know the code and was afraid
> > some other function somewhere would use psy->dev. If you think it is
> > safer this way I'll resend the patch.
>
> Neither is a proper fix, unfortunately. :-( Without some external lock
> you can't use psy->dev as a flag to check if the registration was
> successful. There is really no point trying to force core functions
> to keep psy->dev in a sane state after registration has failed.
>
> So, instead of patching power_supply core, I'd suggest the
> following patch (on top of 2/3 and 3/3, so far only compile-
> tested). How does it look?
Comments below. Thanks for adding this lock.
> diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
> index 2ae2fca..475e17c 100644
> --- a/drivers/acpi/battery.c
> +++ b/drivers/acpi/battery.c
> @@ -101,6 +101,7 @@ enum {
>
> struct acpi_battery {
> struct mutex lock;
> + struct mutex bat_lock;
> struct power_supply bat;
> struct acpi_device *device;
> struct notifier_block pm_nb;
> @@ -559,8 +560,10 @@ static int sysfs_add_battery(struct acpi_battery *battery)
> battery->bat.get_property = acpi_battery_get_property;
>
> result = power_supply_register(&battery->device->dev, &battery->bat);
> - if (result)
> + if (result) {
> + battery->bat.dev = NULL;
> return result;
> + }
> return device_create_file(battery->bat.dev, &alarm_attr);
> }
>
> @@ -613,31 +616,40 @@ static int acpi_battery_update(struct acpi_battery *battery)
> result = acpi_battery_get_status(battery);
> if (result)
> return result;
> +
> + mutex_lock(&battery->bat_lock);
> +
> if (!acpi_battery_present(battery)) {
> sysfs_remove_battery(battery);
> battery->update_time = 0;
> - return 0;
> + result = 0;
> + goto out_unlock;
> }
> if (!battery->update_time ||
> old_present != acpi_battery_present(battery)) {
> result = acpi_battery_get_info(battery);
> if (result)
> - return result;
> + goto out_unlock;
> acpi_battery_quirks(battery);
> acpi_battery_init_alarm(battery);
> }
> if (!battery->bat.dev) {
> result = sysfs_add_battery(battery);
> if (result)
> - return result;
> + goto out_unlock;
> }
> result = acpi_battery_get_state(battery);
> acpi_battery_quirks2(battery);
> +
> +out_unlock:
> + mutex_unlock(&battery->bat_lock);
> return result;
> }
>
> -static void acpi_battery_refresh(struct acpi_battery *battery)
> +static void sysfs_readd_battery(struct acpi_battery *battery)
s/readd/read/?
> {
> + mutex_lock(&battery->bat_lock);
> +
> if (!battery->bat.dev)
> return;
Remember to unlock before return:
if (!battery->bat.dev)
goto out;
>
> @@ -645,6 +657,13 @@ static void acpi_battery_refresh(struct acpi_battery *battery)
> /* The battery may have changed its reporting units. */
> sysfs_remove_battery(battery);
> sysfs_add_battery(battery);
> +
out:
> + mutex_unlock(&battery->bat_lock);
> +}
Stefan
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] ACPI / Battery: avoid acpi_battery_add() use-after-free
2011-07-12 8:03 ` [PATCH 2/3] ACPI / Battery: avoid acpi_battery_add() use-after-free Stefan Hajnoczi
@ 2011-07-16 22:56 ` Len Brown
0 siblings, 0 replies; 10+ messages in thread
From: Len Brown @ 2011-07-16 22:56 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: Anton Vorontsov, David Woodhouse, linux-acpi, linux-kernel
applied to acpi-test
(on top of existing battery patches)
thanks,
Len Brown, Intel Open Source Technology Center
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 3/3] ACPI / Battery: propagate sysfs error in acpi_battery_add()
2011-07-12 8:03 ` [PATCH 3/3] ACPI / Battery: propagate sysfs error in acpi_battery_add() Stefan Hajnoczi
@ 2011-07-16 22:58 ` Len Brown
0 siblings, 0 replies; 10+ messages in thread
From: Len Brown @ 2011-07-16 22:58 UTC (permalink / raw)
To: Stefan Hajnoczi
Cc: Anton Vorontsov, David Woodhouse, linux-acpi, linux-kernel
applied, after merging with acpi-test.
merged version appended below.
thanks,
Len Brown, Intel Open Source Technology Center
>From eb03cb02b74df6dd0b653d5f6d976f16a434dfaf Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Date: Tue, 12 Jul 2011 09:03:29 +0100
Subject: [PATCH] ACPI / Battery: propagate sysfs error in acpi_battery_add()
X-Patchwork-Hint: ignore
Make sure the error return from sysfs_add_battery() is checked and
propagated out from acpi_battery_add().
Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/battery.c | 11 ++++++++---
1 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index c771768..ffce2f0 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -630,8 +630,11 @@ static int acpi_battery_update(struct acpi_battery *battery)
return result;
acpi_battery_init_alarm(battery);
}
- if (!battery->bat.dev)
- sysfs_add_battery(battery);
+ if (!battery->bat.dev) {
+ result = sysfs_add_battery(battery);
+ if (result)
+ return result;
+ }
result = acpi_battery_get_state(battery);
acpi_battery_quirks(battery);
return result;
@@ -982,7 +985,9 @@ static int acpi_battery_add(struct acpi_device *device)
if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle,
"_BIX", &handle)))
set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
- acpi_battery_update(battery);
+ result = acpi_battery_update(battery);
+ if (result)
+ goto fail;
#ifdef CONFIG_ACPI_PROCFS_POWER
result = acpi_battery_add_fs(device);
#endif
--
1.7.6.178.g55272
^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2011-07-16 22:58 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-07-12 8:03 [PATCH 0/3] ACPI / Battery: fix NULL pointer dereference from battery Stefan Hajnoczi
2011-07-12 8:03 ` [PATCH 1/3] power_supply: scrub device pointer if registration fails Stefan Hajnoczi
2011-07-12 15:09 ` Anton Vorontsov
2011-07-12 15:30 ` Stefan Hajnoczi
2011-07-12 17:23 ` Anton Vorontsov
2011-07-14 6:07 ` Stefan Hajnoczi
2011-07-12 8:03 ` [PATCH 2/3] ACPI / Battery: avoid acpi_battery_add() use-after-free Stefan Hajnoczi
2011-07-16 22:56 ` Len Brown
2011-07-12 8:03 ` [PATCH 3/3] ACPI / Battery: propagate sysfs error in acpi_battery_add() Stefan Hajnoczi
2011-07-16 22:58 ` Len Brown
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox