diff options
author | Jeff Layton <jlayton@primarydata.com> | 2014-07-16 06:52:19 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2014-08-03 17:05:23 -0400 |
commit | c5e6aecd034e70e73af4d7a2b8a115239e3568b5 (patch) | |
tree | eebbcf35eb1eceaa76b022a64b134b51dd31b62a /net | |
parent | a3b255717fed1cad0dd4ed5be77114d32ef22a6d (diff) |
sunrpc: fix RCU handling of gc_ctx field
The handling of the gc_ctx pointer only seems to be partially RCU-safe.
The assignment and freeing are done using RCU, but many places in the
code seem to dereference that pointer without proper RCU safeguards.
Fix them to use rcu_dereference and to rcu_read_lock/unlock, and to
properly handle the case where the pointer is NULL.
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Jeff Layton <jlayton@primarydata.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 52 |
1 files changed, 35 insertions, 17 deletions
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 73854314fb85..afb292cd797d 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c | |||
@@ -183,8 +183,9 @@ gss_cred_get_ctx(struct rpc_cred *cred) | |||
183 | struct gss_cl_ctx *ctx = NULL; | 183 | struct gss_cl_ctx *ctx = NULL; |
184 | 184 | ||
185 | rcu_read_lock(); | 185 | rcu_read_lock(); |
186 | if (gss_cred->gc_ctx) | 186 | ctx = rcu_dereference(gss_cred->gc_ctx); |
187 | ctx = gss_get_ctx(gss_cred->gc_ctx); | 187 | if (ctx) |
188 | gss_get_ctx(ctx); | ||
188 | rcu_read_unlock(); | 189 | rcu_read_unlock(); |
189 | return ctx; | 190 | return ctx; |
190 | } | 191 | } |
@@ -1207,13 +1208,13 @@ gss_destroying_context(struct rpc_cred *cred) | |||
1207 | { | 1208 | { |
1208 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); | 1209 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); |
1209 | struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); | 1210 | struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); |
1211 | struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1); | ||
1210 | struct rpc_task *task; | 1212 | struct rpc_task *task; |
1211 | 1213 | ||
1212 | if (gss_cred->gc_ctx == NULL || | 1214 | if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) |
1213 | test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) | ||
1214 | return 0; | 1215 | return 0; |
1215 | 1216 | ||
1216 | gss_cred->gc_ctx->gc_proc = RPC_GSS_PROC_DESTROY; | 1217 | ctx->gc_proc = RPC_GSS_PROC_DESTROY; |
1217 | cred->cr_ops = &gss_nullops; | 1218 | cred->cr_ops = &gss_nullops; |
1218 | 1219 | ||
1219 | /* Take a reference to ensure the cred will be destroyed either | 1220 | /* Take a reference to ensure the cred will be destroyed either |
@@ -1274,7 +1275,7 @@ gss_destroy_nullcred(struct rpc_cred *cred) | |||
1274 | { | 1275 | { |
1275 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); | 1276 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); |
1276 | struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); | 1277 | struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); |
1277 | struct gss_cl_ctx *ctx = gss_cred->gc_ctx; | 1278 | struct gss_cl_ctx *ctx = rcu_dereference_protected(gss_cred->gc_ctx, 1); |
1278 | 1279 | ||
1279 | RCU_INIT_POINTER(gss_cred->gc_ctx, NULL); | 1280 | RCU_INIT_POINTER(gss_cred->gc_ctx, NULL); |
1280 | call_rcu(&cred->cr_rcu, gss_free_cred_callback); | 1281 | call_rcu(&cred->cr_rcu, gss_free_cred_callback); |
@@ -1349,20 +1350,30 @@ gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred) | |||
1349 | static char * | 1350 | static char * |
1350 | gss_stringify_acceptor(struct rpc_cred *cred) | 1351 | gss_stringify_acceptor(struct rpc_cred *cred) |
1351 | { | 1352 | { |
1352 | char *string; | 1353 | char *string = NULL; |
1353 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); | 1354 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); |
1354 | struct xdr_netobj *acceptor = &gss_cred->gc_ctx->gc_acceptor; | 1355 | struct gss_cl_ctx *ctx; |
1356 | struct xdr_netobj *acceptor; | ||
1357 | |||
1358 | rcu_read_lock(); | ||
1359 | ctx = rcu_dereference(gss_cred->gc_ctx); | ||
1360 | if (!ctx) | ||
1361 | goto out; | ||
1362 | |||
1363 | acceptor = &ctx->gc_acceptor; | ||
1355 | 1364 | ||
1356 | /* no point if there's no string */ | 1365 | /* no point if there's no string */ |
1357 | if (!acceptor->len) | 1366 | if (!acceptor->len) |
1358 | return NULL; | 1367 | goto out; |
1359 | 1368 | ||
1360 | string = kmalloc(acceptor->len + 1, GFP_KERNEL); | 1369 | string = kmalloc(acceptor->len + 1, GFP_KERNEL); |
1361 | if (!string) | 1370 | if (!string) |
1362 | return string; | 1371 | goto out; |
1363 | 1372 | ||
1364 | memcpy(string, acceptor->data, acceptor->len); | 1373 | memcpy(string, acceptor->data, acceptor->len); |
1365 | string[acceptor->len] = '\0'; | 1374 | string[acceptor->len] = '\0'; |
1375 | out: | ||
1376 | rcu_read_unlock(); | ||
1366 | return string; | 1377 | return string; |
1367 | } | 1378 | } |
1368 | 1379 | ||
@@ -1374,15 +1385,16 @@ static int | |||
1374 | gss_key_timeout(struct rpc_cred *rc) | 1385 | gss_key_timeout(struct rpc_cred *rc) |
1375 | { | 1386 | { |
1376 | struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); | 1387 | struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); |
1388 | struct gss_cl_ctx *ctx; | ||
1377 | unsigned long now = jiffies; | 1389 | unsigned long now = jiffies; |
1378 | unsigned long expire; | 1390 | unsigned long expire; |
1379 | 1391 | ||
1380 | if (gss_cred->gc_ctx == NULL) | 1392 | rcu_read_lock(); |
1381 | return -EACCES; | 1393 | ctx = rcu_dereference(gss_cred->gc_ctx); |
1382 | 1394 | if (ctx) | |
1383 | expire = gss_cred->gc_ctx->gc_expiry - (gss_key_expire_timeo * HZ); | 1395 | expire = ctx->gc_expiry - (gss_key_expire_timeo * HZ); |
1384 | 1396 | rcu_read_unlock(); | |
1385 | if (time_after(now, expire)) | 1397 | if (!ctx || time_after(now, expire)) |
1386 | return -EACCES; | 1398 | return -EACCES; |
1387 | return 0; | 1399 | return 0; |
1388 | } | 1400 | } |
@@ -1391,13 +1403,19 @@ static int | |||
1391 | gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags) | 1403 | gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags) |
1392 | { | 1404 | { |
1393 | struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); | 1405 | struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); |
1406 | struct gss_cl_ctx *ctx; | ||
1394 | int ret; | 1407 | int ret; |
1395 | 1408 | ||
1396 | if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags)) | 1409 | if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags)) |
1397 | goto out; | 1410 | goto out; |
1398 | /* Don't match with creds that have expired. */ | 1411 | /* Don't match with creds that have expired. */ |
1399 | if (time_after(jiffies, gss_cred->gc_ctx->gc_expiry)) | 1412 | rcu_read_lock(); |
1413 | ctx = rcu_dereference(gss_cred->gc_ctx); | ||
1414 | if (!ctx || time_after(jiffies, ctx->gc_expiry)) { | ||
1415 | rcu_read_unlock(); | ||
1400 | return 0; | 1416 | return 0; |
1417 | } | ||
1418 | rcu_read_unlock(); | ||
1401 | if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags)) | 1419 | if (!test_bit(RPCAUTH_CRED_UPTODATE, &rc->cr_flags)) |
1402 | return 0; | 1420 | return 0; |
1403 | out: | 1421 | out: |