Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

regulator: fix memory leak on error path of regulator_register()

The change corrects registration and deregistration on error path
of a regulator, the problem was manifested by a reported memory
leak on deferred probe:

as3722-regulator as3722-regulator: regulator 13 register failed -517

# cat /sys/kernel/debug/kmemleak
unreferenced object 0xecc43740 (size 64):
comm "swapper/0", pid 1, jiffies 4294937640 (age 712.880s)
hex dump (first 32 bytes):
72 65 67 75 6c 61 74 6f 72 2e 32 34 00 5a 5a 5a regulator.24.ZZZ
5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a ZZZZZZZZZZZZZZZZ
backtrace:
[<0c4c3d1c>] __kmalloc_track_caller+0x15c/0x2c0
[<40c0ad48>] kvasprintf+0x64/0xd4
[<109abd29>] kvasprintf_const+0x70/0x84
[<c4215946>] kobject_set_name_vargs+0x34/0xa8
[<62282ea2>] dev_set_name+0x40/0x64
[<a39b6757>] regulator_register+0x3a4/0x1344
[<16a9543f>] devm_regulator_register+0x4c/0x84
[<51a4c6a1>] as3722_regulator_probe+0x294/0x754
...

The memory leak problem was introduced as a side ef another fix in
regulator_register() error path, I believe that the proper fix is
to decouple device_register() function into its two compounds and
initialize a struct device before assigning any values to its fields
and then using it before actual registration of a device happens.

This lets to call put_device() safely after initialization, and, since
now a release callback is called, kfree(rdev->constraints) shall be
removed to exclude a double free condition.

Fixes: a3cde9534ebd ("regulator: core: fix regulator_register() error paths to properly release rdev")
Signed-off-by: Vladimir Zapolskiy <vz@mleia.com>
Cc: Wen Yang <wenyang@linux.alibaba.com>
Link: https://lore.kernel.org/r/20200724005013.23278-1-vz@mleia.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Vladimir Zapolskiy and committed by
Mark Brown
9177514c 2ca76b3e

+7 -11
+7 -11
drivers/regulator/core.c
··· 5092 5092 struct regulator_dev *rdev; 5093 5093 bool dangling_cfg_gpiod = false; 5094 5094 bool dangling_of_gpiod = false; 5095 - bool reg_device_fail = false; 5096 5095 struct device *dev; 5097 5096 int ret, i; 5098 5097 ··· 5220 5221 } 5221 5222 5222 5223 /* register with sysfs */ 5224 + device_initialize(&rdev->dev); 5223 5225 rdev->dev.class = &regulator_class; 5224 5226 rdev->dev.parent = dev; 5225 5227 dev_set_name(&rdev->dev, "regulator.%lu", 5226 5228 (unsigned long) atomic_inc_return(&regulator_no)); 5229 + dev_set_drvdata(&rdev->dev, rdev); 5227 5230 5228 5231 /* set regulator constraints */ 5229 5232 if (init_data) ··· 5276 5275 !rdev->desc->fixed_uV) 5277 5276 rdev->is_switch = true; 5278 5277 5279 - dev_set_drvdata(&rdev->dev, rdev); 5280 - ret = device_register(&rdev->dev); 5281 - if (ret != 0) { 5282 - reg_device_fail = true; 5278 + ret = device_add(&rdev->dev); 5279 + if (ret != 0) 5283 5280 goto unset_supplies; 5284 - } 5285 5281 5286 5282 rdev_init_debugfs(rdev); 5287 5283 ··· 5300 5302 mutex_unlock(&regulator_list_mutex); 5301 5303 wash: 5302 5304 kfree(rdev->coupling_desc.coupled_rdevs); 5303 - kfree(rdev->constraints); 5304 5305 mutex_lock(&regulator_list_mutex); 5305 5306 regulator_ena_gpio_free(rdev); 5306 5307 mutex_unlock(&regulator_list_mutex); 5308 + put_device(&rdev->dev); 5309 + rdev = NULL; 5307 5310 clean: 5308 5311 if (dangling_of_gpiod) 5309 5312 gpiod_put(config->ena_gpiod); 5310 - if (reg_device_fail) 5311 - put_device(&rdev->dev); 5312 - else 5313 - kfree(rdev); 5313 + kfree(rdev); 5314 5314 kfree(config); 5315 5315 rinse: 5316 5316 if (dangling_cfg_gpiod)