* [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
* 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
* [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
* 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
* [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 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