aboutsummaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2015-04-07 09:27:01 -0400
committerHerbert Xu <herbert@gondor.apana.org.au>2015-04-08 10:20:06 -0400
commit016baaa1183bb0c5fb2a7de42413bba8a51c1bc8 (patch)
treeb6a1705e4cf6a1d504532630fe923ef261f6b2a5 /crypto
parent9cd223239a79df3cc758ecabb8473ca91599021b (diff)
crypto: user - Fix crypto_alg_match race
The function crypto_alg_match returns an algorithm without taking any references on it. This means that the algorithm can be freed at any time, therefore all users of crypto_alg_match are buggy. This patch fixes this by taking a reference count on the algorithm to prevent such races. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto')
-rw-r--r--crypto/crypto_user.c39
1 files changed, 29 insertions, 10 deletions
diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c
index eab249723830..41dfe762b7fb 100644
--- a/crypto/crypto_user.c
+++ b/crypto/crypto_user.c
@@ -62,10 +62,14 @@ static struct crypto_alg *crypto_alg_match(struct crypto_user_alg *p, int exact)
62 else if (!exact) 62 else if (!exact)
63 match = !strcmp(q->cra_name, p->cru_name); 63 match = !strcmp(q->cra_name, p->cru_name);
64 64
65 if (match) { 65 if (!match)
66 alg = q; 66 continue;
67 break; 67
68 } 68 if (unlikely(!crypto_mod_get(q)))
69 continue;
70
71 alg = q;
72 break;
69 } 73 }
70 74
71 up_read(&crypto_alg_sem); 75 up_read(&crypto_alg_sem);
@@ -205,9 +209,10 @@ static int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,
205 if (!alg) 209 if (!alg)
206 return -ENOENT; 210 return -ENOENT;
207 211
212 err = -ENOMEM;
208 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 213 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
209 if (!skb) 214 if (!skb)
210 return -ENOMEM; 215 goto drop_alg;
211 216
212 info.in_skb = in_skb; 217 info.in_skb = in_skb;
213 info.out_skb = skb; 218 info.out_skb = skb;
@@ -215,6 +220,10 @@ static int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh,
215 info.nlmsg_flags = 0; 220 info.nlmsg_flags = 0;
216 221
217 err = crypto_report_alg(alg, &info); 222 err = crypto_report_alg(alg, &info);
223
224drop_alg:
225 crypto_mod_put(alg);
226
218 if (err) 227 if (err)
219 return err; 228 return err;
220 229
@@ -284,6 +293,7 @@ static int crypto_update_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
284 293
285 up_write(&crypto_alg_sem); 294 up_write(&crypto_alg_sem);
286 295
296 crypto_mod_put(alg);
287 crypto_remove_final(&list); 297 crypto_remove_final(&list);
288 298
289 return 0; 299 return 0;
@@ -294,6 +304,7 @@ static int crypto_del_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
294{ 304{
295 struct crypto_alg *alg; 305 struct crypto_alg *alg;
296 struct crypto_user_alg *p = nlmsg_data(nlh); 306 struct crypto_user_alg *p = nlmsg_data(nlh);
307 int err;
297 308
298 if (!netlink_capable(skb, CAP_NET_ADMIN)) 309 if (!netlink_capable(skb, CAP_NET_ADMIN))
299 return -EPERM; 310 return -EPERM;
@@ -310,13 +321,19 @@ static int crypto_del_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
310 * if we try to unregister. Unregistering such an algorithm without 321 * if we try to unregister. Unregistering such an algorithm without
311 * removing the module is not possible, so we restrict to crypto 322 * removing the module is not possible, so we restrict to crypto
312 * instances that are build from templates. */ 323 * instances that are build from templates. */
324 err = -EINVAL;
313 if (!(alg->cra_flags & CRYPTO_ALG_INSTANCE)) 325 if (!(alg->cra_flags & CRYPTO_ALG_INSTANCE))
314 return -EINVAL; 326 goto drop_alg;
315 327
316 if (atomic_read(&alg->cra_refcnt) != 1) 328 err = -EBUSY;
317 return -EBUSY; 329 if (atomic_read(&alg->cra_refcnt) > 2)
330 goto drop_alg;
318 331
319 return crypto_unregister_instance((struct crypto_instance *)alg); 332 err = crypto_unregister_instance((struct crypto_instance *)alg);
333
334drop_alg:
335 crypto_mod_put(alg);
336 return err;
320} 337}
321 338
322static struct crypto_alg *crypto_user_skcipher_alg(const char *name, u32 type, 339static struct crypto_alg *crypto_user_skcipher_alg(const char *name, u32 type,
@@ -395,8 +412,10 @@ static int crypto_add_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
395 return -EINVAL; 412 return -EINVAL;
396 413
397 alg = crypto_alg_match(p, exact); 414 alg = crypto_alg_match(p, exact);
398 if (alg) 415 if (alg) {
416 crypto_mod_put(alg);
399 return -EEXIST; 417 return -EEXIST;
418 }
400 419
401 if (strlen(p->cru_driver_name)) 420 if (strlen(p->cru_driver_name))
402 name = p->cru_driver_name; 421 name = p->cru_driver_name;