Re: [PATCH] hw_random: move add_early_randomness() out of rng_mutex

From: Marek Szyprowski
Date: Fri Oct 11 2019 - 04:45:28 EST


Hi Laurent,

On 12.09.2019 15:30, Laurent Vivier wrote:
> add_early_randomness() is called every time a new rng backend is added
> and every time it is set as the current rng provider.
>
> add_early_randomness() is called from functions locking rng_mutex,
> and if it hangs all the hw_random framework hangs: we can't read sysfs,
> add or remove a backend.
>
> This patch move add_early_randomness() out of the rng_mutex zone.
> It only needs the reading_mutex.
>
> Signed-off-by: Laurent Vivier <lvivier@xxxxxxxxxx>

This patch landed in today's linux-next and causes the following warning
on ARM 32bit Exynos5420-based Chromebook Peach-Pit board:

tpm_i2c_infineon 9-0020: 1.2 TPM (device-id 0x1A)
------------[ cut here ]------------
WARNING: CPU: 3 PID: 1 at lib/refcount.c:156 hwrng_register+0x13c/0x1b4
refcount_t: increment on 0; use-after-free.
Modules linked in:
CPU: 3 PID: 1 Comm: swapper/0 Not tainted 5.4.0-rc1-00061-gdaae28debcb0
#6714
Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[<c01124c8>] (unwind_backtrace) from [<c010dfb8>] (show_stack+0x10/0x14)
[<c010dfb8>] (show_stack) from [<c0ae86d8>] (dump_stack+0xa8/0xd4)
[<c0ae86d8>] (dump_stack) from [<c0127428>] (__warn+0xf4/0x10c)
[<c0127428>] (__warn) from [<c01274b4>] (warn_slowpath_fmt+0x74/0xb8)
[<c01274b4>] (warn_slowpath_fmt) from [<c054729c>]
(hwrng_register+0x13c/0x1b4)
[<c054729c>] (hwrng_register) from [<c0547e54>]
(tpm_chip_register+0xc4/0x274)
[<c0547e54>] (tpm_chip_register) from [<c055028c>]
(tpm_tis_i2c_probe+0x138/0x1a0)
[<c055028c>] (tpm_tis_i2c_probe) from [<c07324b0>]
(i2c_device_probe+0x230/0x2a4)
[<c07324b0>] (i2c_device_probe) from [<c05c1884>] (really_probe+0x1c4/0x48c)
[<c05c1884>] (really_probe) from [<c05c1d20>]
(driver_probe_device+0x78/0x1f8)
[<c05c1d20>] (driver_probe_device) from [<c05bf7cc>]
(bus_for_each_drv+0x74/0xb8)
[<c05bf7cc>] (bus_for_each_drv) from [<c05c1620>]
(__device_attach+0xd4/0x16c)
[<c05c1620>] (__device_attach) from [<c05c0790>]
(bus_probe_device+0x88/0x90)
[<c05c0790>] (bus_probe_device) from [<c05bdff8>] (device_add+0x478/0x62c)
[<c05bdff8>] (device_add) from [<c0734928>]
(i2c_new_client_device+0x158/0x278)
[<c0734928>] (i2c_new_client_device) from [<c0734a50>]
(i2c_new_device+0x8/0x14)
[<c0734a50>] (i2c_new_device) from [<c0738014>]
(of_i2c_register_devices+0xf4/0x16c)
[<c0738014>] (of_i2c_register_devices) from [<c0734f34>]
(i2c_register_adapter+0x180/0x458)
[<c0734f34>] (i2c_register_adapter) from [<c073b6c0>]
(exynos5_i2c_probe+0x22c/0x28c)
[<c073b6c0>] (exynos5_i2c_probe) from [<c05c410c>]
(platform_drv_probe+0x6c/0xa4)
[<c05c410c>] (platform_drv_probe) from [<c05c1884>]
(really_probe+0x1c4/0x48c)
[<c05c1884>] (really_probe) from [<c05c1d20>]
(driver_probe_device+0x78/0x1f8)
[<c05c1d20>] (driver_probe_device) from [<c05c2104>]
(device_driver_attach+0x58/0x60)
[<c05c2104>] (device_driver_attach) from [<c05c21e8>]
(__driver_attach+0xdc/0x174)
[<c05c21e8>] (__driver_attach) from [<c05bf6f8>]
(bus_for_each_dev+0x68/0xb4)
[<c05bf6f8>] (bus_for_each_dev) from [<c05c0a2c>]
(bus_add_driver+0x158/0x214)
[<c05c0a2c>] (bus_add_driver) from [<c05c30bc>] (driver_register+0x78/0x110)
[<c05c30bc>] (driver_register) from [<c0103214>]
(do_one_initcall+0x8c/0x424)
[<c0103214>] (do_one_initcall) from [<c1001080>]
(kernel_init_freeable+0x158/0x200)
[<c1001080>] (kernel_init_freeable) from [<c0b036a8>]
(kernel_init+0x8/0x114)
[<c0b036a8>] (kernel_init) from [<c01010b4>] (ret_from_fork+0x14/0x20)
Exception stack(0xe88e1fb0 to 0xe88e1ff8)
1fa0:ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ 00000000 00000000 00000000
00000000
1fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
1fe0: 00000000 00000000 00000000 00000000 00000013 00000000
irq event stamp: 296027
hardirqs last enabled at (296157): [<c0b0bce8>]
_raw_spin_unlock_irq+0x24/0x5c
hardirqs last disabled at (296180): [<c0b04030>] __schedule+0xd8/0x7b8
softirqs last enabled at (296176): [<c01026bc>] __do_softirq+0x4fc/0x5fc
softirqs last disabled at (296197): [<c012fff4>] irq_exit+0x16c/0x170
---[ end trace d219e40773096999 ]---

If you need any help testing a fix for this issue, let me know.


> ---
> drivers/char/hw_random/core.c | 60 +++++++++++++++++++++++++----------
> 1 file changed, 44 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
> index 9044d31ab1a1..745ace6fffd7 100644
> --- a/drivers/char/hw_random/core.c
> +++ b/drivers/char/hw_random/core.c
> @@ -111,6 +111,14 @@ static void drop_current_rng(void)
> }
>
> /* Returns ERR_PTR(), NULL or refcounted hwrng */
> +static struct hwrng *get_current_rng_nolock(void)
> +{
> + if (current_rng)
> + kref_get(&current_rng->ref);
> +
> + return current_rng;
> +}
> +
> static struct hwrng *get_current_rng(void)
> {
> struct hwrng *rng;
> @@ -118,9 +126,7 @@ static struct hwrng *get_current_rng(void)
> if (mutex_lock_interruptible(&rng_mutex))
> return ERR_PTR(-ERESTARTSYS);
>
> - rng = current_rng;
> - if (rng)
> - kref_get(&rng->ref);
> + rng = get_current_rng_nolock();
>
> mutex_unlock(&rng_mutex);
> return rng;
> @@ -155,8 +161,6 @@ static int hwrng_init(struct hwrng *rng)
> reinit_completion(&rng->cleanup_done);
>
> skip_init:
> - add_early_randomness(rng);
> -
> current_quality = rng->quality ? : default_quality;
> if (current_quality > 1024)
> current_quality = 1024;
> @@ -320,12 +324,13 @@ static ssize_t hwrng_attr_current_store(struct device *dev,
> const char *buf, size_t len)
> {
> int err = -ENODEV;
> - struct hwrng *rng;
> + struct hwrng *rng, *old_rng, *new_rng;
>
> err = mutex_lock_interruptible(&rng_mutex);
> if (err)
> return -ERESTARTSYS;
>
> + old_rng = current_rng;
> if (sysfs_streq(buf, "")) {
> err = enable_best_rng();
> } else {
> @@ -337,9 +342,15 @@ static ssize_t hwrng_attr_current_store(struct device *dev,
> }
> }
> }
> -
> + new_rng = get_current_rng_nolock();
> mutex_unlock(&rng_mutex);
>
> + if (new_rng) {
> + if (new_rng != old_rng)
> + add_early_randomness(new_rng);
> + put_rng(new_rng);
> + }
> +
> return err ? : len;
> }
>
> @@ -457,13 +468,17 @@ static void start_khwrngd(void)
> int hwrng_register(struct hwrng *rng)
> {
> int err = -EINVAL;
> - struct hwrng *old_rng, *tmp;
> + struct hwrng *old_rng, *new_rng, *tmp;
> struct list_head *rng_list_ptr;
>
> if (!rng->name || (!rng->data_read && !rng->read))
> goto out;
>
> mutex_lock(&rng_mutex);
> +
> + old_rng = current_rng;
> + new_rng = NULL;
> +
> /* Must not register two RNGs with the same name. */
> err = -EEXIST;
> list_for_each_entry(tmp, &rng_list, list) {
> @@ -482,7 +497,6 @@ int hwrng_register(struct hwrng *rng)
> }
> list_add_tail(&rng->list, rng_list_ptr);
>
> - old_rng = current_rng;
> err = 0;
> if (!old_rng ||
> (!cur_rng_set_by_user && rng->quality > old_rng->quality)) {
> @@ -496,19 +510,24 @@ int hwrng_register(struct hwrng *rng)
> goto out_unlock;
> }
>
> - if (old_rng && !rng->init) {
> + new_rng = rng;
> + kref_get(&new_rng->ref);
> +out_unlock:
> + mutex_unlock(&rng_mutex);
> +
> + if (new_rng) {
> + if (new_rng != old_rng || !rng->init) {
> /*
> * Use a new device's input to add some randomness to
> * the system. If this rng device isn't going to be
> * used right away, its init function hasn't been
> - * called yet; so only use the randomness from devices
> - * that don't need an init callback.
> + * called yet by set_current_rng(); so only use the
> + * randomness from devices that don't need an init callback
> */
> - add_early_randomness(rng);
> + add_early_randomness(new_rng);
> + }
> + put_rng(new_rng);
> }
> -
> -out_unlock:
> - mutex_unlock(&rng_mutex);
> out:
> return err;
> }
> @@ -516,10 +535,12 @@ EXPORT_SYMBOL_GPL(hwrng_register);
>
> void hwrng_unregister(struct hwrng *rng)
> {
> + struct hwrng *old_rng, *new_rng;
> int err;
>
> mutex_lock(&rng_mutex);
>
> + old_rng = current_rng;
> list_del(&rng->list);
> if (current_rng == rng) {
> err = enable_best_rng();
> @@ -529,6 +550,7 @@ void hwrng_unregister(struct hwrng *rng)
> }
> }
>
> + new_rng = get_current_rng_nolock();
> if (list_empty(&rng_list)) {
> mutex_unlock(&rng_mutex);
> if (hwrng_fill)
> @@ -536,6 +558,12 @@ void hwrng_unregister(struct hwrng *rng)
> } else
> mutex_unlock(&rng_mutex);
>
> + if (new_rng) {
> + if (old_rng != new_rng)
> + add_early_randomness(new_rng);
> + put_rng(new_rng);
> + }
> +
> wait_for_completion(&rng->cleanup_done);
> }
> EXPORT_SYMBOL_GPL(hwrng_unregister);

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland