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 <lviv...@redhat.com>

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

Reply via email to