diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2014-12-23 16:40:18 +1100 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2014-12-26 08:33:34 +1100 |
commit | 15b66cd54291186011f733cc750263f320b8a0a4 (patch) | |
tree | 3b95b884c9381f5fb6a7ea4286df2288d61ba4ac /drivers/char/hw_random | |
parent | 77584ee57434813b50fc85cde995a6271a5081b7 (diff) | |
download | linux-15b66cd54291186011f733cc750263f320b8a0a4.tar.bz2 |
hwrng: core - Fix current_rng init/cleanup race yet again
The kref solution is still buggy because we were only focusing
on the register/unregister race. The same race affects the
setting of current_rng through sysfs.
This patch fixes it by using kref_get_unless_zero.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'drivers/char/hw_random')
-rw-r--r-- | drivers/char/hw_random/core.c | 14 |
1 files changed, 11 insertions, 3 deletions
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 3dba2cf50241..42827fd5f38d 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -105,7 +105,6 @@ static inline void cleanup_rng(struct kref *kref) static void set_current_rng(struct hwrng *rng) { BUG_ON(!mutex_is_locked(&rng_mutex)); - kref_get(&rng->ref); current_rng = rng; } @@ -150,6 +149,9 @@ static void put_rng(struct hwrng *rng) static inline int hwrng_init(struct hwrng *rng) { + if (kref_get_unless_zero(&rng->ref)) + goto skip_init; + if (rng->init) { int ret; @@ -157,6 +159,11 @@ static inline int hwrng_init(struct hwrng *rng) if (ret) return ret; } + + kref_init(&rng->ref); + reinit_completion(&rng->cleanup_done); + +skip_init: add_early_randomness(rng); current_quality = rng->quality ? : default_quality; @@ -467,6 +474,9 @@ int hwrng_register(struct hwrng *rng) goto out_unlock; } + init_completion(&rng->cleanup_done); + complete(&rng->cleanup_done); + old_rng = current_rng; err = 0; if (!old_rng) { @@ -494,8 +504,6 @@ int hwrng_register(struct hwrng *rng) add_early_randomness(rng); } - init_completion(&rng->cleanup_done); - out_unlock: mutex_unlock(&rng_mutex); out: |