Message ID | E1Y3ICg-0007cq-Kj@gondolin.me.apana.org.au (mailing list archive) |
---|---|
State | Accepted |
Delegated to: | Herbert Xu |
Headers | show |
Herbert Xu <herbert@gondor.apana.org.au> writes: > 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> This patch scares me a little! I'll have to pull the tree to review it properly, but the theory was that the reference count was counting users of the rng. Now I don't know what it's counting: > static inline int hwrng_init(struct hwrng *rng) > { > + if (kref_get_unless_zero(&rng->ref)) > + goto skip_init; > + > if (rng->init) { > int ret; OK, so this skip_init branch is triggered when the rng is being shut down as it's no longer current_rng? > + > + kref_init(&rng->ref); > + reinit_completion(&rng->cleanup_done); > + > +skip_init: > add_early_randomness(rng); Then we use it to add randomness? > > 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); > + This code smells very bad. Cheers, Rusty. -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Dec 24, 2014 at 09:56:36AM +1030, Rusty Russell wrote: > > I'll have to pull the tree to review it properly, but the theory was > that the reference count was counting users of the rng. Now I don't > know what it's counting: The reference count starts off at zero, meaning that the RNG has not been initialised. It becomes one when we do hwrng_init. After that each user adds/subtracts from it. When it hits zero we will clean it up and await for either deregistration or the next hwrng_init. > > static inline int hwrng_init(struct hwrng *rng) > > { > > + if (kref_get_unless_zero(&rng->ref)) > > + goto skip_init; > > + > > if (rng->init) { > > int ret; > > OK, so this skip_init branch is triggered when the rng is being > shut down as it's no longer current_rng? Right, if it triggers it means that we have already been initialised previously and we have not yet executed cleanup on the RNG even though at some point another RNG came in and became current_rng. Bottom line is that this RNG is still good and we don't need to (can't) initialise it. > > + > > + kref_init(&rng->ref); > > + reinit_completion(&rng->cleanup_done); > > + > > +skip_init: > > add_early_randomness(rng); > > Then we use it to add randomness? Honestly I don't care about whether we add randomness or not in this case but this is what the code did before. If you dislike that feel free to send in a patch to kill this too. > > @@ -467,6 +474,9 @@ int hwrng_register(struct hwrng *rng) > > goto out_unlock; > > } > > > > + init_completion(&rng->cleanup_done); > > + complete(&rng->cleanup_done); > > + > > This code smells very bad. Well, it would smell better if someone added a helper that lets you initialise a completion that is already complete :) Point is the RNG must only be in two states. Either it's the current RNG in which case cleanup_done must be false/incomplete, or it's not and cleanup_done must be either already complete or will be complete at some point in the future. Cheers,
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 3dba2cf..42827fd 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:
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> --- drivers/char/hw_random/core.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html