diff options
author | Andreas Gruenbacher <agruenba@redhat.com> | 2016-11-15 05:06:40 -0500 |
---|---|---|
committer | Paul Moore <paul@paul-moore.com> | 2016-11-22 17:44:02 -0500 |
commit | 9287aed2ad1ff1bde5eb190bcd6dccd5f1cf47d3 (patch) | |
tree | 0d57e7ecdbf0d7c2ac9c377a4a53c5c9b135d38d | |
parent | 3322d0d64f4e942862a152f6f11137a1f5eac2e9 (diff) |
selinux: Convert isec->lock into a spinlock
Convert isec->lock from a mutex into a spinlock. Instead of holding
the lock while sleeping in inode_doinit_with_dentry, set
isec->initialized to LABEL_PENDING and release the lock. Then, when
the sid has been determined, re-acquire the lock. If isec->initialized
is still set to LABEL_PENDING, set isec->sid; otherwise, the sid has
been set by another task (LABEL_INITIALIZED) or invalidated
(LABEL_INVALID) in the meantime.
This fixes a deadlock on gfs2 where
* one task is in inode_doinit_with_dentry -> gfs2_getxattr, holds
isec->lock, and tries to acquire the inode's glock, and
* another task is in do_xmote -> inode_go_inval ->
selinux_inode_invalidate_secctx, holds the inode's glock, and
tries to acquire isec->lock.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
[PM: minor tweaks to keep checkpatch.pl happy]
Signed-off-by: Paul Moore <paul@paul-moore.com>
-rw-r--r-- | security/selinux/hooks.c | 101 | ||||
-rw-r--r-- | security/selinux/include/objsec.h | 5 |
2 files changed, 66 insertions, 40 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2a506861a772..98a2e92b3168 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -231,7 +231,7 @@ static int inode_alloc_security(struct inode *inode) | |||
231 | if (!isec) | 231 | if (!isec) |
232 | return -ENOMEM; | 232 | return -ENOMEM; |
233 | 233 | ||
234 | mutex_init(&isec->lock); | 234 | spin_lock_init(&isec->lock); |
235 | INIT_LIST_HEAD(&isec->list); | 235 | INIT_LIST_HEAD(&isec->list); |
236 | isec->inode = inode; | 236 | isec->inode = inode; |
237 | isec->sid = SECINITSID_UNLABELED; | 237 | isec->sid = SECINITSID_UNLABELED; |
@@ -1382,7 +1382,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
1382 | { | 1382 | { |
1383 | struct superblock_security_struct *sbsec = NULL; | 1383 | struct superblock_security_struct *sbsec = NULL; |
1384 | struct inode_security_struct *isec = inode->i_security; | 1384 | struct inode_security_struct *isec = inode->i_security; |
1385 | u32 sid; | 1385 | u32 task_sid, sid = 0; |
1386 | u16 sclass; | ||
1386 | struct dentry *dentry; | 1387 | struct dentry *dentry; |
1387 | #define INITCONTEXTLEN 255 | 1388 | #define INITCONTEXTLEN 255 |
1388 | char *context = NULL; | 1389 | char *context = NULL; |
@@ -1392,7 +1393,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
1392 | if (isec->initialized == LABEL_INITIALIZED) | 1393 | if (isec->initialized == LABEL_INITIALIZED) |
1393 | return 0; | 1394 | return 0; |
1394 | 1395 | ||
1395 | mutex_lock(&isec->lock); | 1396 | spin_lock(&isec->lock); |
1396 | if (isec->initialized == LABEL_INITIALIZED) | 1397 | if (isec->initialized == LABEL_INITIALIZED) |
1397 | goto out_unlock; | 1398 | goto out_unlock; |
1398 | 1399 | ||
@@ -1411,12 +1412,18 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
1411 | goto out_unlock; | 1412 | goto out_unlock; |
1412 | } | 1413 | } |
1413 | 1414 | ||
1415 | sclass = isec->sclass; | ||
1416 | task_sid = isec->task_sid; | ||
1417 | sid = isec->sid; | ||
1418 | isec->initialized = LABEL_PENDING; | ||
1419 | spin_unlock(&isec->lock); | ||
1420 | |||
1414 | switch (sbsec->behavior) { | 1421 | switch (sbsec->behavior) { |
1415 | case SECURITY_FS_USE_NATIVE: | 1422 | case SECURITY_FS_USE_NATIVE: |
1416 | break; | 1423 | break; |
1417 | case SECURITY_FS_USE_XATTR: | 1424 | case SECURITY_FS_USE_XATTR: |
1418 | if (!(inode->i_opflags & IOP_XATTR)) { | 1425 | if (!(inode->i_opflags & IOP_XATTR)) { |
1419 | isec->sid = sbsec->def_sid; | 1426 | sid = sbsec->def_sid; |
1420 | break; | 1427 | break; |
1421 | } | 1428 | } |
1422 | /* Need a dentry, since the xattr API requires one. | 1429 | /* Need a dentry, since the xattr API requires one. |
@@ -1438,7 +1445,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
1438 | * inode_doinit with a dentry, before these inodes could | 1445 | * inode_doinit with a dentry, before these inodes could |
1439 | * be used again by userspace. | 1446 | * be used again by userspace. |
1440 | */ | 1447 | */ |
1441 | goto out_unlock; | 1448 | goto out; |
1442 | } | 1449 | } |
1443 | 1450 | ||
1444 | len = INITCONTEXTLEN; | 1451 | len = INITCONTEXTLEN; |
@@ -1446,7 +1453,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
1446 | if (!context) { | 1453 | if (!context) { |
1447 | rc = -ENOMEM; | 1454 | rc = -ENOMEM; |
1448 | dput(dentry); | 1455 | dput(dentry); |
1449 | goto out_unlock; | 1456 | goto out; |
1450 | } | 1457 | } |
1451 | context[len] = '\0'; | 1458 | context[len] = '\0'; |
1452 | rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); | 1459 | rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); |
@@ -1457,14 +1464,14 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
1457 | rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); | 1464 | rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); |
1458 | if (rc < 0) { | 1465 | if (rc < 0) { |
1459 | dput(dentry); | 1466 | dput(dentry); |
1460 | goto out_unlock; | 1467 | goto out; |
1461 | } | 1468 | } |
1462 | len = rc; | 1469 | len = rc; |
1463 | context = kmalloc(len+1, GFP_NOFS); | 1470 | context = kmalloc(len+1, GFP_NOFS); |
1464 | if (!context) { | 1471 | if (!context) { |
1465 | rc = -ENOMEM; | 1472 | rc = -ENOMEM; |
1466 | dput(dentry); | 1473 | dput(dentry); |
1467 | goto out_unlock; | 1474 | goto out; |
1468 | } | 1475 | } |
1469 | context[len] = '\0'; | 1476 | context[len] = '\0'; |
1470 | rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); | 1477 | rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); |
@@ -1476,7 +1483,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
1476 | "%d for dev=%s ino=%ld\n", __func__, | 1483 | "%d for dev=%s ino=%ld\n", __func__, |
1477 | -rc, inode->i_sb->s_id, inode->i_ino); | 1484 | -rc, inode->i_sb->s_id, inode->i_ino); |
1478 | kfree(context); | 1485 | kfree(context); |
1479 | goto out_unlock; | 1486 | goto out; |
1480 | } | 1487 | } |
1481 | /* Map ENODATA to the default file SID */ | 1488 | /* Map ENODATA to the default file SID */ |
1482 | sid = sbsec->def_sid; | 1489 | sid = sbsec->def_sid; |
@@ -1506,28 +1513,25 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
1506 | } | 1513 | } |
1507 | } | 1514 | } |
1508 | kfree(context); | 1515 | kfree(context); |
1509 | isec->sid = sid; | ||
1510 | break; | 1516 | break; |
1511 | case SECURITY_FS_USE_TASK: | 1517 | case SECURITY_FS_USE_TASK: |
1512 | isec->sid = isec->task_sid; | 1518 | sid = task_sid; |
1513 | break; | 1519 | break; |
1514 | case SECURITY_FS_USE_TRANS: | 1520 | case SECURITY_FS_USE_TRANS: |
1515 | /* Default to the fs SID. */ | 1521 | /* Default to the fs SID. */ |
1516 | isec->sid = sbsec->sid; | 1522 | sid = sbsec->sid; |
1517 | 1523 | ||
1518 | /* Try to obtain a transition SID. */ | 1524 | /* Try to obtain a transition SID. */ |
1519 | rc = security_transition_sid(isec->task_sid, sbsec->sid, | 1525 | rc = security_transition_sid(task_sid, sid, sclass, NULL, &sid); |
1520 | isec->sclass, NULL, &sid); | ||
1521 | if (rc) | 1526 | if (rc) |
1522 | goto out_unlock; | 1527 | goto out; |
1523 | isec->sid = sid; | ||
1524 | break; | 1528 | break; |
1525 | case SECURITY_FS_USE_MNTPOINT: | 1529 | case SECURITY_FS_USE_MNTPOINT: |
1526 | isec->sid = sbsec->mntpoint_sid; | 1530 | sid = sbsec->mntpoint_sid; |
1527 | break; | 1531 | break; |
1528 | default: | 1532 | default: |
1529 | /* Default to the fs superblock SID. */ | 1533 | /* Default to the fs superblock SID. */ |
1530 | isec->sid = sbsec->sid; | 1534 | sid = sbsec->sid; |
1531 | 1535 | ||
1532 | if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) { | 1536 | if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) { |
1533 | /* We must have a dentry to determine the label on | 1537 | /* We must have a dentry to determine the label on |
@@ -1550,21 +1554,30 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
1550 | * could be used again by userspace. | 1554 | * could be used again by userspace. |
1551 | */ | 1555 | */ |
1552 | if (!dentry) | 1556 | if (!dentry) |
1553 | goto out_unlock; | 1557 | goto out; |
1554 | rc = selinux_genfs_get_sid(dentry, isec->sclass, | 1558 | rc = selinux_genfs_get_sid(dentry, sclass, |
1555 | sbsec->flags, &sid); | 1559 | sbsec->flags, &sid); |
1556 | dput(dentry); | 1560 | dput(dentry); |
1557 | if (rc) | 1561 | if (rc) |
1558 | goto out_unlock; | 1562 | goto out; |
1559 | isec->sid = sid; | ||
1560 | } | 1563 | } |
1561 | break; | 1564 | break; |
1562 | } | 1565 | } |
1563 | 1566 | ||
1564 | isec->initialized = LABEL_INITIALIZED; | 1567 | out: |
1568 | spin_lock(&isec->lock); | ||
1569 | if (isec->initialized == LABEL_PENDING) { | ||
1570 | if (!sid || rc) { | ||
1571 | isec->initialized = LABEL_INVALID; | ||
1572 | goto out_unlock; | ||
1573 | } | ||
1574 | |||
1575 | isec->initialized = LABEL_INITIALIZED; | ||
1576 | isec->sid = sid; | ||
1577 | } | ||
1565 | 1578 | ||
1566 | out_unlock: | 1579 | out_unlock: |
1567 | mutex_unlock(&isec->lock); | 1580 | spin_unlock(&isec->lock); |
1568 | return rc; | 1581 | return rc; |
1569 | } | 1582 | } |
1570 | 1583 | ||
@@ -3195,9 +3208,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, | |||
3195 | } | 3208 | } |
3196 | 3209 | ||
3197 | isec = backing_inode_security(dentry); | 3210 | isec = backing_inode_security(dentry); |
3211 | spin_lock(&isec->lock); | ||
3198 | isec->sclass = inode_mode_to_security_class(inode->i_mode); | 3212 | isec->sclass = inode_mode_to_security_class(inode->i_mode); |
3199 | isec->sid = newsid; | 3213 | isec->sid = newsid; |
3200 | isec->initialized = LABEL_INITIALIZED; | 3214 | isec->initialized = LABEL_INITIALIZED; |
3215 | spin_unlock(&isec->lock); | ||
3201 | 3216 | ||
3202 | return; | 3217 | return; |
3203 | } | 3218 | } |
@@ -3290,9 +3305,11 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, | |||
3290 | if (rc) | 3305 | if (rc) |
3291 | return rc; | 3306 | return rc; |
3292 | 3307 | ||
3308 | spin_lock(&isec->lock); | ||
3293 | isec->sclass = inode_mode_to_security_class(inode->i_mode); | 3309 | isec->sclass = inode_mode_to_security_class(inode->i_mode); |
3294 | isec->sid = newsid; | 3310 | isec->sid = newsid; |
3295 | isec->initialized = LABEL_INITIALIZED; | 3311 | isec->initialized = LABEL_INITIALIZED; |
3312 | spin_unlock(&isec->lock); | ||
3296 | return 0; | 3313 | return 0; |
3297 | } | 3314 | } |
3298 | 3315 | ||
@@ -3953,9 +3970,11 @@ static void selinux_task_to_inode(struct task_struct *p, | |||
3953 | struct inode_security_struct *isec = inode->i_security; | 3970 | struct inode_security_struct *isec = inode->i_security; |
3954 | u32 sid = task_sid(p); | 3971 | u32 sid = task_sid(p); |
3955 | 3972 | ||
3973 | spin_lock(&isec->lock); | ||
3956 | isec->sclass = inode_mode_to_security_class(inode->i_mode); | 3974 | isec->sclass = inode_mode_to_security_class(inode->i_mode); |
3957 | isec->sid = sid; | 3975 | isec->sid = sid; |
3958 | isec->initialized = LABEL_INITIALIZED; | 3976 | isec->initialized = LABEL_INITIALIZED; |
3977 | spin_unlock(&isec->lock); | ||
3959 | } | 3978 | } |
3960 | 3979 | ||
3961 | /* Returns error only if unable to parse addresses */ | 3980 | /* Returns error only if unable to parse addresses */ |
@@ -4274,24 +4293,24 @@ static int selinux_socket_post_create(struct socket *sock, int family, | |||
4274 | const struct task_security_struct *tsec = current_security(); | 4293 | const struct task_security_struct *tsec = current_security(); |
4275 | struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock)); | 4294 | struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock)); |
4276 | struct sk_security_struct *sksec; | 4295 | struct sk_security_struct *sksec; |
4296 | u16 sclass = socket_type_to_security_class(family, type, protocol); | ||
4297 | u32 sid = SECINITSID_KERNEL; | ||
4277 | int err = 0; | 4298 | int err = 0; |
4278 | 4299 | ||
4279 | isec->sclass = socket_type_to_security_class(family, type, protocol); | 4300 | if (!kern) { |
4280 | 4301 | err = socket_sockcreate_sid(tsec, sclass, &sid); | |
4281 | if (kern) | ||
4282 | isec->sid = SECINITSID_KERNEL; | ||
4283 | else { | ||
4284 | err = socket_sockcreate_sid(tsec, isec->sclass, &(isec->sid)); | ||
4285 | if (err) | 4302 | if (err) |
4286 | return err; | 4303 | return err; |
4287 | } | 4304 | } |
4288 | 4305 | ||
4306 | isec->sclass = sclass; | ||
4307 | isec->sid = sid; | ||
4289 | isec->initialized = LABEL_INITIALIZED; | 4308 | isec->initialized = LABEL_INITIALIZED; |
4290 | 4309 | ||
4291 | if (sock->sk) { | 4310 | if (sock->sk) { |
4292 | sksec = sock->sk->sk_security; | 4311 | sksec = sock->sk->sk_security; |
4293 | sksec->sid = isec->sid; | 4312 | sksec->sclass = sclass; |
4294 | sksec->sclass = isec->sclass; | 4313 | sksec->sid = sid; |
4295 | err = selinux_netlbl_socket_post_create(sock->sk, family); | 4314 | err = selinux_netlbl_socket_post_create(sock->sk, family); |
4296 | } | 4315 | } |
4297 | 4316 | ||
@@ -4467,16 +4486,22 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | |||
4467 | int err; | 4486 | int err; |
4468 | struct inode_security_struct *isec; | 4487 | struct inode_security_struct *isec; |
4469 | struct inode_security_struct *newisec; | 4488 | struct inode_security_struct *newisec; |
4489 | u16 sclass; | ||
4490 | u32 sid; | ||
4470 | 4491 | ||
4471 | err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); | 4492 | err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); |
4472 | if (err) | 4493 | if (err) |
4473 | return err; | 4494 | return err; |
4474 | 4495 | ||
4475 | newisec = inode_security_novalidate(SOCK_INODE(newsock)); | ||
4476 | |||
4477 | isec = inode_security_novalidate(SOCK_INODE(sock)); | 4496 | isec = inode_security_novalidate(SOCK_INODE(sock)); |
4478 | newisec->sclass = isec->sclass; | 4497 | spin_lock(&isec->lock); |
4479 | newisec->sid = isec->sid; | 4498 | sclass = isec->sclass; |
4499 | sid = isec->sid; | ||
4500 | spin_unlock(&isec->lock); | ||
4501 | |||
4502 | newisec = inode_security_novalidate(SOCK_INODE(newsock)); | ||
4503 | newisec->sclass = sclass; | ||
4504 | newisec->sid = sid; | ||
4480 | newisec->initialized = LABEL_INITIALIZED; | 4505 | newisec->initialized = LABEL_INITIALIZED; |
4481 | 4506 | ||
4482 | return 0; | 4507 | return 0; |
@@ -5979,9 +6004,9 @@ static void selinux_inode_invalidate_secctx(struct inode *inode) | |||
5979 | { | 6004 | { |
5980 | struct inode_security_struct *isec = inode->i_security; | 6005 | struct inode_security_struct *isec = inode->i_security; |
5981 | 6006 | ||
5982 | mutex_lock(&isec->lock); | 6007 | spin_lock(&isec->lock); |
5983 | isec->initialized = LABEL_INVALID; | 6008 | isec->initialized = LABEL_INVALID; |
5984 | mutex_unlock(&isec->lock); | 6009 | spin_unlock(&isec->lock); |
5985 | } | 6010 | } |
5986 | 6011 | ||
5987 | /* | 6012 | /* |
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index c21e135460a5..e8dab0f02c72 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h | |||
@@ -39,7 +39,8 @@ struct task_security_struct { | |||
39 | 39 | ||
40 | enum label_initialized { | 40 | enum label_initialized { |
41 | LABEL_INVALID, /* invalid or not initialized */ | 41 | LABEL_INVALID, /* invalid or not initialized */ |
42 | LABEL_INITIALIZED /* initialized */ | 42 | LABEL_INITIALIZED, /* initialized */ |
43 | LABEL_PENDING | ||
43 | }; | 44 | }; |
44 | 45 | ||
45 | struct inode_security_struct { | 46 | struct inode_security_struct { |
@@ -52,7 +53,7 @@ struct inode_security_struct { | |||
52 | u32 sid; /* SID of this object */ | 53 | u32 sid; /* SID of this object */ |
53 | u16 sclass; /* security class of this object */ | 54 | u16 sclass; /* security class of this object */ |
54 | unsigned char initialized; /* initialization flag */ | 55 | unsigned char initialized; /* initialization flag */ |
55 | struct mutex lock; | 56 | spinlock_t lock; |
56 | }; | 57 | }; |
57 | 58 | ||
58 | struct file_security_struct { | 59 | struct file_security_struct { |