Skip to content

Commit 7f1a57f

Browse files
krzksre
authored andcommitted
power_supply: Fix possible NULL pointer dereference on early uevent
Don't call the power_supply_changed() from power_supply_register() when parent is still probing because it may lead to accessing parent too early. In bq27x00_battery this caused NULL pointer exception because uevent of power_supply_changed called back the the get_property() method provided by the driver. The get_property() method accessed pointer which should be returned by power_supply_register(). Starting from bq27x00_battery_probe(): di->bat = power_supply_register() power_supply_changed() kobject_uevent() power_supply_uevent() power_supply_show_property() power_supply_get_property() bq27x00_battery_get_property() dereference of di->bat which is NULL here The dereference of di->bat (value returned by power_supply_register()) is the currently visible problem. However calling back the methods provided by driver before ending the probe may lead to accessing other driver-related data which is not yet initialized. The call to power_supply_changed() is postponed till probing ends - mutex of parent device is released. Reported-by: H. Nikolaus Schaller <[email protected]> Signed-off-by: Krzysztof Kozlowski <[email protected]> Fixes: 297d716 ("power_supply: Change ownership from driver to core") Tested-By: Dr. H. Nikolaus Schaller <[email protected]> Signed-off-by: Sebastian Reichel <[email protected]>
1 parent 8e59c7f commit 7f1a57f

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

drivers/power/power_supply_core.c

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ EXPORT_SYMBOL_GPL(power_supply_notifier);
3030

3131
static struct device_type power_supply_dev_type;
3232

33+
#define POWER_SUPPLY_DEFERRED_REGISTER_TIME msecs_to_jiffies(10)
34+
3335
static bool __power_supply_is_supplied_by(struct power_supply *supplier,
3436
struct power_supply *supply)
3537
{
@@ -121,6 +123,30 @@ void power_supply_changed(struct power_supply *psy)
121123
}
122124
EXPORT_SYMBOL_GPL(power_supply_changed);
123125

126+
/*
127+
* Notify that power supply was registered after parent finished the probing.
128+
*
129+
* Often power supply is registered from driver's probe function. However
130+
* calling power_supply_changed() directly from power_supply_register()
131+
* would lead to execution of get_property() function provided by the driver
132+
* too early - before the probe ends.
133+
*
134+
* Avoid that by waiting on parent's mutex.
135+
*/
136+
static void power_supply_deferred_register_work(struct work_struct *work)
137+
{
138+
struct power_supply *psy = container_of(work, struct power_supply,
139+
deferred_register_work.work);
140+
141+
if (psy->dev.parent)
142+
mutex_lock(&psy->dev.parent->mutex);
143+
144+
power_supply_changed(psy);
145+
146+
if (psy->dev.parent)
147+
mutex_unlock(&psy->dev.parent->mutex);
148+
}
149+
124150
#ifdef CONFIG_OF
125151
#include <linux/of.h>
126152

@@ -645,6 +671,10 @@ __power_supply_register(struct device *parent,
645671
struct power_supply *psy;
646672
int rc;
647673

674+
if (!parent)
675+
pr_warn("%s: Expected proper parent device for '%s'\n",
676+
__func__, desc->name);
677+
648678
psy = kzalloc(sizeof(*psy), GFP_KERNEL);
649679
if (!psy)
650680
return ERR_PTR(-ENOMEM);
@@ -671,6 +701,8 @@ __power_supply_register(struct device *parent,
671701
goto dev_set_name_failed;
672702

673703
INIT_WORK(&psy->changed_work, power_supply_changed_work);
704+
INIT_DELAYED_WORK(&psy->deferred_register_work,
705+
power_supply_deferred_register_work);
674706

675707
rc = power_supply_check_supplies(psy);
676708
if (rc) {
@@ -709,7 +741,10 @@ __power_supply_register(struct device *parent,
709741
* after calling power_supply_register()).
710742
*/
711743
atomic_inc(&psy->use_cnt);
712-
power_supply_changed(psy);
744+
745+
queue_delayed_work(system_power_efficient_wq,
746+
&psy->deferred_register_work,
747+
POWER_SUPPLY_DEFERRED_REGISTER_TIME);
713748

714749
return psy;
715750

@@ -729,7 +764,8 @@ __power_supply_register(struct device *parent,
729764

730765
/**
731766
* power_supply_register() - Register new power supply
732-
* @parent: Device to be a parent of power supply's device
767+
* @parent: Device to be a parent of power supply's device, usually
768+
* the device which probe function calls this
733769
* @desc: Description of power supply, must be valid through whole
734770
* lifetime of this power supply
735771
* @cfg: Run-time specific configuration accessed during registering,
@@ -750,7 +786,8 @@ EXPORT_SYMBOL_GPL(power_supply_register);
750786

751787
/**
752788
* power_supply_register() - Register new non-waking-source power supply
753-
* @parent: Device to be a parent of power supply's device
789+
* @parent: Device to be a parent of power supply's device, usually
790+
* the device which probe function calls this
754791
* @desc: Description of power supply, must be valid through whole
755792
* lifetime of this power supply
756793
* @cfg: Run-time specific configuration accessed during registering,
@@ -779,7 +816,8 @@ static void devm_power_supply_release(struct device *dev, void *res)
779816

780817
/**
781818
* power_supply_register() - Register managed power supply
782-
* @parent: Device to be a parent of power supply's device
819+
* @parent: Device to be a parent of power supply's device, usually
820+
* the device which probe function calls this
783821
* @desc: Description of power supply, must be valid through whole
784822
* lifetime of this power supply
785823
* @cfg: Run-time specific configuration accessed during registering,
@@ -814,7 +852,8 @@ EXPORT_SYMBOL_GPL(devm_power_supply_register);
814852

815853
/**
816854
* power_supply_register() - Register managed non-waking-source power supply
817-
* @parent: Device to be a parent of power supply's device
855+
* @parent: Device to be a parent of power supply's device, usually
856+
* the device which probe function calls this
818857
* @desc: Description of power supply, must be valid through whole
819858
* lifetime of this power supply
820859
* @cfg: Run-time specific configuration accessed during registering,
@@ -858,6 +897,7 @@ void power_supply_unregister(struct power_supply *psy)
858897
{
859898
WARN_ON(atomic_dec_return(&psy->use_cnt));
860899
cancel_work_sync(&psy->changed_work);
900+
cancel_delayed_work_sync(&psy->deferred_register_work);
861901
sysfs_remove_link(&psy->dev.kobj, "powers");
862902
power_supply_remove_triggers(psy);
863903
psy_unregister_cooler(psy);

include/linux/power_supply.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ struct power_supply {
237237
/* private */
238238
struct device dev;
239239
struct work_struct changed_work;
240+
struct delayed_work deferred_register_work;
240241
spinlock_t changed_lock;
241242
bool changed;
242243
atomic_t use_cnt;

0 commit comments

Comments
 (0)