diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2014-12-23 00:40:18 -0500 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2014-12-25 16:33:34 -0500 |
commit | 15b66cd54291186011f733cc750263f320b8a0a4 (patch) | |
tree | 3b95b884c9381f5fb6a7ea4286df2288d61ba4ac | |
parent | 77584ee57434813b50fc85cde995a6271a5081b7 (diff) |
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>
-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) | |||
105 | static void set_current_rng(struct hwrng *rng) | 105 | static void set_current_rng(struct hwrng *rng) |
106 | { | 106 | { |
107 | BUG_ON(!mutex_is_locked(&rng_mutex)); | 107 | BUG_ON(!mutex_is_locked(&rng_mutex)); |
108 | kref_get(&rng->ref); | ||
109 | current_rng = rng; | 108 | current_rng = rng; |
110 | } | 109 | } |
111 | 110 | ||
@@ -150,6 +149,9 @@ static void put_rng(struct hwrng *rng) | |||
150 | 149 | ||
151 | static inline int hwrng_init(struct hwrng *rng) | 150 | static inline int hwrng_init(struct hwrng *rng) |
152 | { | 151 | { |
152 | if (kref_get_unless_zero(&rng->ref)) | ||
153 | goto skip_init; | ||
154 | |||
153 | if (rng->init) { | 155 | if (rng->init) { |
154 | int ret; | 156 | int ret; |
155 | 157 | ||
@@ -157,6 +159,11 @@ static inline int hwrng_init(struct hwrng *rng) | |||
157 | if (ret) | 159 | if (ret) |
158 | return ret; | 160 | return ret; |
159 | } | 161 | } |
162 | |||
163 | kref_init(&rng->ref); | ||
164 | reinit_completion(&rng->cleanup_done); | ||
165 | |||
166 | skip_init: | ||
160 | add_early_randomness(rng); | 167 | add_early_randomness(rng); |
161 | 168 | ||
162 | current_quality = rng->quality ? : default_quality; | 169 | current_quality = rng->quality ? : default_quality; |
@@ -467,6 +474,9 @@ int hwrng_register(struct hwrng *rng) | |||
467 | goto out_unlock; | 474 | goto out_unlock; |
468 | } | 475 | } |
469 | 476 | ||
477 | init_completion(&rng->cleanup_done); | ||
478 | complete(&rng->cleanup_done); | ||
479 | |||
470 | old_rng = current_rng; | 480 | old_rng = current_rng; |
471 | err = 0; | 481 | err = 0; |
472 | if (!old_rng) { | 482 | if (!old_rng) { |
@@ -494,8 +504,6 @@ int hwrng_register(struct hwrng *rng) | |||
494 | add_early_randomness(rng); | 504 | add_early_randomness(rng); |
495 | } | 505 | } |
496 | 506 | ||
497 | init_completion(&rng->cleanup_done); | ||
498 | |||
499 | out_unlock: | 507 | out_unlock: |
500 | mutex_unlock(&rng_mutex); | 508 | mutex_unlock(&rng_mutex); |
501 | out: | 509 | out: |