Skip to content

Commit 6d8c50d

Browse files
congwangdavem330
authored andcommitted
socket: close race condition between sock_close() and sockfs_setattr()
fchownat() doesn't even hold refcnt of fd until it figures out fd is really needed (otherwise is ignored) and releases it after it resolves the path. This means sock_close() could race with sockfs_setattr(), which leads to a NULL pointer dereference since typically we set sock->sk to NULL in ->release(). As pointed out by Al, this is unique to sockfs. So we can fix this in socket layer by acquiring inode_lock in sock_close() and checking against NULL in sockfs_setattr(). sock_release() is called in many places, only the sock_close() path matters here. And fortunately, this should not affect normal sock_close() as it is only called when the last fd refcnt is gone. It only affects sock_close() with a parallel sockfs_setattr() in progress, which is not common. Fixes: 86741ec ("net: core: Add a UID field to struct sock.") Reported-by: shankarapailoor <[email protected]> Cc: Tetsuo Handa <[email protected]> Cc: Lorenzo Colitti <[email protected]> Cc: Al Viro <[email protected]> Signed-off-by: Cong Wang <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 873aca2 commit 6d8c50d

File tree

1 file changed

+15
-3
lines changed

1 file changed

+15
-3
lines changed

net/socket.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,10 @@ static int sockfs_setattr(struct dentry *dentry, struct iattr *iattr)
541541
if (!err && (iattr->ia_valid & ATTR_UID)) {
542542
struct socket *sock = SOCKET_I(d_inode(dentry));
543543

544-
sock->sk->sk_uid = iattr->ia_uid;
544+
if (sock->sk)
545+
sock->sk->sk_uid = iattr->ia_uid;
546+
else
547+
err = -ENOENT;
545548
}
546549

547550
return err;
@@ -590,12 +593,16 @@ EXPORT_SYMBOL(sock_alloc);
590593
* an inode not a file.
591594
*/
592595

593-
void sock_release(struct socket *sock)
596+
static void __sock_release(struct socket *sock, struct inode *inode)
594597
{
595598
if (sock->ops) {
596599
struct module *owner = sock->ops->owner;
597600

601+
if (inode)
602+
inode_lock(inode);
598603
sock->ops->release(sock);
604+
if (inode)
605+
inode_unlock(inode);
599606
sock->ops = NULL;
600607
module_put(owner);
601608
}
@@ -609,6 +616,11 @@ void sock_release(struct socket *sock)
609616
}
610617
sock->file = NULL;
611618
}
619+
620+
void sock_release(struct socket *sock)
621+
{
622+
__sock_release(sock, NULL);
623+
}
612624
EXPORT_SYMBOL(sock_release);
613625

614626
void __sock_tx_timestamp(__u16 tsflags, __u8 *tx_flags)
@@ -1171,7 +1183,7 @@ static int sock_mmap(struct file *file, struct vm_area_struct *vma)
11711183

11721184
static int sock_close(struct inode *inode, struct file *filp)
11731185
{
1174-
sock_release(SOCKET_I(inode));
1186+
__sock_release(SOCKET_I(inode), inode);
11751187
return 0;
11761188
}
11771189

0 commit comments

Comments
 (0)