Skip to content

Commit 8f3cbcd

Browse files
ChiYuan Huangbroonie
authored andcommitted
regulator: core: Use different devices for resource allocation and DT lookup
Following by the below discussion, there's the potential UAF issue between regulator and mfd. https://lore.kernel.org/all/20221128143601.1698148-1-yangyingliang@huawei.com/ From the analysis of Yingliang CPU A |CPU B mt6370_probe() | devm_mfd_add_devices() | |mt6370_regulator_probe() | regulator_register() | //allocate init_data and add it to devres | regulator_of_get_init_data() i2c_unregister_device() | device_del() | devres_release_all() | // init_data is freed | release_nodes() | | // using init_data causes UAF | regulator_register() It's common to use mfd core to create child device for the regulator. In order to do the DT lookup for init data, the child that registered the regulator would pass its parent as the parameter. And this causes init data resource allocated to its parent, not itself. The issue happen when parent device is going to release and regulator core is still doing some operation of init data constraint for the regulator of child device. To fix it, this patch expand 'regulator_register' API to use the different devices for init data allocation and DT lookup. Reported-by: Yang Yingliang <yangyingliang@huawei.com> Signed-off-by: ChiYuan Huang <cy_huang@richtek.com> Link: https://lore.kernel.org/r/1670311341-32664-1-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 2a17ddf commit 8f3cbcd

File tree

6 files changed

+11
-9
lines changed

6 files changed

+11
-9
lines changed

drivers/platform/x86/intel/int3472/clk_and_regulator.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
185185
cfg.init_data = &init_data;
186186
cfg.ena_gpiod = int3472->regulator.gpio;
187187

188-
int3472->regulator.rdev = regulator_register(&int3472->regulator.rdesc,
188+
int3472->regulator.rdev = regulator_register(int3472->dev,
189+
&int3472->regulator.rdesc,
189190
&cfg);
190191
if (IS_ERR(int3472->regulator.rdev)) {
191192
ret = PTR_ERR(int3472->regulator.rdev);

drivers/regulator/core.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5409,6 +5409,7 @@ static struct regulator_coupler generic_regulator_coupler = {
54095409

54105410
/**
54115411
* regulator_register - register regulator
5412+
* @dev: the device that drive the regulator
54125413
* @regulator_desc: regulator to register
54135414
* @cfg: runtime configuration for regulator
54145415
*
@@ -5417,7 +5418,8 @@ static struct regulator_coupler generic_regulator_coupler = {
54175418
* or an ERR_PTR() on error.
54185419
*/
54195420
struct regulator_dev *
5420-
regulator_register(const struct regulator_desc *regulator_desc,
5421+
regulator_register(struct device *dev,
5422+
const struct regulator_desc *regulator_desc,
54215423
const struct regulator_config *cfg)
54225424
{
54235425
const struct regulator_init_data *init_data;
@@ -5426,7 +5428,6 @@ regulator_register(const struct regulator_desc *regulator_desc,
54265428
struct regulator_dev *rdev;
54275429
bool dangling_cfg_gpiod = false;
54285430
bool dangling_of_gpiod = false;
5429-
struct device *dev;
54305431
int ret, i;
54315432
bool resolved_early = false;
54325433

@@ -5439,8 +5440,7 @@ regulator_register(const struct regulator_desc *regulator_desc,
54395440
goto rinse;
54405441
}
54415442

5442-
dev = cfg->dev;
5443-
WARN_ON(!dev);
5443+
WARN_ON(!dev || !cfg->dev);
54445444

54455445
if (regulator_desc->name == NULL || regulator_desc->ops == NULL) {
54465446
ret = -EINVAL;

drivers/regulator/devres.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ struct regulator_dev *devm_regulator_register(struct device *dev,
415415
if (!ptr)
416416
return ERR_PTR(-ENOMEM);
417417

418-
rdev = regulator_register(regulator_desc, config);
418+
rdev = regulator_register(dev, regulator_desc, config);
419419
if (!IS_ERR(rdev)) {
420420
*ptr = rdev;
421421
devres_add(dev, ptr);

drivers/regulator/of_regulator.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
505505
struct device_node *child;
506506
struct regulator_init_data *init_data = NULL;
507507

508-
child = regulator_of_get_init_node(dev, desc);
508+
child = regulator_of_get_init_node(config->dev, desc);
509509
if (!child)
510510
return NULL;
511511

drivers/regulator/stm32-vrefbuf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev)
210210
pdev->dev.of_node,
211211
&stm32_vrefbuf_regu);
212212

213-
rdev = regulator_register(&stm32_vrefbuf_regu, &config);
213+
rdev = regulator_register(&pdev->dev, &stm32_vrefbuf_regu, &config);
214214
if (IS_ERR(rdev)) {
215215
ret = PTR_ERR(rdev);
216216
dev_err(&pdev->dev, "register failed with error %d\n", ret);

include/linux/regulator/driver.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,8 @@ static inline int regulator_err2notif(int err)
687687

688688

689689
struct regulator_dev *
690-
regulator_register(const struct regulator_desc *regulator_desc,
690+
regulator_register(struct device *dev,
691+
const struct regulator_desc *regulator_desc,
691692
const struct regulator_config *config);
692693
struct regulator_dev *
693694
devm_regulator_register(struct device *dev,

0 commit comments

Comments
 (0)