linux/fs
Will Deacon ec576895d6 chardev: Avoid potential use-after-free in 'chrdev_open()'
commit 68faa679b8 upstream.

'chrdev_open()' calls 'cdev_get()' to obtain a reference to the
'struct cdev *' stashed in the 'i_cdev' field of the target inode
structure. If the pointer is NULL, then it is initialised lazily by
looking up the kobject in the 'cdev_map' and so the whole procedure is
protected by the 'cdev_lock' spinlock to serialise initialisation of
the shared pointer.

Unfortunately, it is possible for the initialising thread to fail *after*
installing the new pointer, for example if the subsequent '->open()' call
on the file fails. In this case, 'cdev_put()' is called, the reference
count on the kobject is dropped and, if nobody else has taken a reference,
the release function is called which finally clears 'inode->i_cdev' from
'cdev_purge()' before potentially freeing the object. The problem here
is that a racing thread can happily take the 'cdev_lock' and see the
non-NULL pointer in the inode, which can result in a refcount increment
from zero and a warning:

  |  ------------[ cut here ]------------
  |  refcount_t: addition on 0; use-after-free.
  |  WARNING: CPU: 2 PID: 6385 at lib/refcount.c:25 refcount_warn_saturate+0x6d/0xf0
  |  Modules linked in:
  |  CPU: 2 PID: 6385 Comm: repro Not tainted 5.5.0-rc2+ #22
  |  Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014
  |  RIP: 0010:refcount_warn_saturate+0x6d/0xf0
  |  Code: 05 55 9a 15 01 01 e8 9d aa c8 ff 0f 0b c3 80 3d 45 9a 15 01 00 75 ce 48 c7 c7 00 9c 62 b3 c6 08
  |  RSP: 0018:ffffb524c1b9bc70 EFLAGS: 00010282
  |  RAX: 0000000000000000 RBX: ffff9e9da1f71390 RCX: 0000000000000000
  |  RDX: ffff9e9dbbd27618 RSI: ffff9e9dbbd18798 RDI: ffff9e9dbbd18798
  |  RBP: 0000000000000000 R08: 000000000000095f R09: 0000000000000039
  |  R10: 0000000000000000 R11: ffffb524c1b9bb20 R12: ffff9e9da1e8c700
  |  R13: ffffffffb25ee8b0 R14: 0000000000000000 R15: ffff9e9da1e8c700
  |  FS:  00007f3b87d26700(0000) GS:ffff9e9dbbd00000(0000) knlGS:0000000000000000
  |  CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
  |  CR2: 00007fc16909c000 CR3: 000000012df9c000 CR4: 00000000000006e0
  |  DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
  |  DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
  |  Call Trace:
  |   kobject_get+0x5c/0x60
  |   cdev_get+0x2b/0x60
  |   chrdev_open+0x55/0x220
  |   ? cdev_put.part.3+0x20/0x20
  |   do_dentry_open+0x13a/0x390
  |   path_openat+0x2c8/0x1470
  |   do_filp_open+0x93/0x100
  |   ? selinux_file_ioctl+0x17f/0x220
  |   do_sys_open+0x186/0x220
  |   do_syscall_64+0x48/0x150
  |   entry_SYSCALL_64_after_hwframe+0x44/0xa9
  |  RIP: 0033:0x7f3b87efcd0e
  |  Code: 89 54 24 08 e8 a3 f4 ff ff 8b 74 24 0c 48 8b 3c 24 41 89 c0 44 8b 54 24 08 b8 01 01 00 00 89 f4
  |  RSP: 002b:00007f3b87d259f0 EFLAGS: 00000293 ORIG_RAX: 0000000000000101
  |  RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f3b87efcd0e
  |  RDX: 0000000000000000 RSI: 00007f3b87d25a80 RDI: 00000000ffffff9c
  |  RBP: 00007f3b87d25e90 R08: 0000000000000000 R09: 0000000000000000
  |  R10: 0000000000000000 R11: 0000000000000293 R12: 00007ffe188f504e
  |  R13: 00007ffe188f504f R14: 00007f3b87d26700 R15: 0000000000000000
  |  ---[ end trace 24f53ca58db8180a ]---

Since 'cdev_get()' can already fail to obtain a reference, simply move
it over to use 'kobject_get_unless_zero()' instead of 'kobject_get()',
which will cause the racing thread to return -ENXIO if the initialising
thread fails unexpectedly.

Cc: Hillf Danton <hdanton@sina.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Reported-by: syzbot+82defefbbd8527e1c2cb@syzkaller.appspotmail.com
Signed-off-by: Will Deacon <will@kernel.org>
Cc: stable <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20191219120203.32691-1-will@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-01-14 20:06:57 +01:00
..
9p 9p: avoid attaching writeback_fid on mmap with type PRIVATE 2019-10-11 18:21:13 +02:00
adfs fs/adfs: super: fix use-after-free bug 2019-08-06 19:06:49 +02:00
affs
afs afs: Fix creation calls in the dynamic root to fail with EOPNOTSUPP 2020-01-09 10:18:57 +01:00
autofs autofs: fix a leak in autofs_expire_indirect() 2019-12-13 08:51:01 +01:00
befs fix a series of Documentation/ broken file name references 2018-06-15 18:10:01 -03:00
bfs bfs: add sanity check at bfs_fill_super() 2018-12-01 09:37:27 +01:00
btrfs btrfs: Fix error messages in qgroup_rescan_init 2020-01-12 12:17:14 +01:00
cachefiles fscache, cachefiles: remove redundant variable 'cache' 2018-12-17 09:24:40 +01:00
ceph ceph: return -EINVAL if given fsc mount option on kernel w/o support 2019-12-05 09:19:45 +01:00
cifs CIFS: Close open handle after interrupted close 2019-12-21 10:57:35 +01:00
coda coda: add error handling for fget 2019-08-06 19:06:51 +02:00
configfs configfs: fix a deadlock in configfs_symlink() 2019-11-12 19:20:47 +01:00
cramfs Cramfs: fix abad comparison when wrap-arounds occur 2018-11-13 11:08:55 -08:00
crypto fscrypt: clean up some BUG_ON()s in block encryption/decryption 2019-07-26 09:14:02 +02:00
debugfs debugfs: fix use-after-free on symlink traversal 2019-05-08 07:21:48 +02:00
devpts fs/devpts: always delete dcache dentry-s in dput() 2019-03-23 20:09:59 +01:00
dlm dlm: fix invalid cluster name warning 2019-12-13 08:52:23 +01:00
ecryptfs ecryptfs_lookup_interpose(): lower_dentry->d_parent is not stable either 2019-11-20 18:45:18 +01:00
efivarfs efivars: Call guid_parse() against guid_t type of variable 2018-07-22 14:13:44 +02:00
efs
exofs exofs_mount(): fix leaks on failure exits 2019-12-05 09:20:32 +01:00
exportfs exportfs_decode_fh(): negative pinned may become positive without the parent locked 2019-12-13 08:51:02 +01:00
ext2 ext2: check err when partial != NULL 2019-12-17 20:35:18 +01:00
ext4 ext4: iomap that extends beyond EOF should be marked dirty 2020-01-04 19:12:47 +01:00
f2fs f2fs: fix to update dir's i_pino during cross_rename 2020-01-04 19:12:50 +01:00
fat fat: work around race with userspace's read via blockdev while mounting 2019-10-07 18:57:14 +02:00
freevxfs
fscache fscache: fix race between enablement and dropping of object 2018-12-17 09:24:40 +01:00
fuse fuse: verify attributes 2019-12-13 08:52:36 +01:00
gfs2 gfs2: fix glock reference problem in gfs2_trans_remove_revoke 2019-12-17 20:35:55 +01:00
hfs fs/hfs/extent.c: fix array out of bounds read of array extent 2019-12-01 09:17:10 +01:00
hfsplus hfsplus: update timestamps on truncate() 2019-12-01 09:17:09 +01:00
hostfs vfs: discard ATTR_ATTR_FLAG 2018-08-17 16:20:28 -07:00
hpfs hpfs: remove unnecessary checks on the value of r when assigning error code 2018-08-25 12:42:33 -07:00
hugetlbfs hugetlb: use same fault hash key for shared and private mappings 2019-05-22 07:37:40 +02:00
isofs isofs: reject hardware sector size > 2048 bytes 2018-08-21 11:37:41 +02:00
jbd2 jbd2: Fix statistics for the number of logged blocks 2020-01-04 19:12:48 +01:00
jffs2 jffs2: fix use-after-free on symlink traversal 2019-05-08 07:21:48 +02:00
jfs Just one jfs patch for 4.19 2018-08-15 22:47:23 -07:00
kernfs kernfs: fix ino wrap-around detection 2019-12-13 08:52:43 +01:00
lockd lockd: fix decoding of TEST results 2019-12-13 08:51:59 +01:00
minix
nfs NFSv4.x: fix lock recovery during delegation recall 2019-11-24 08:20:24 +01:00
nfs_common
nfsd nfsd4: fix up replay_matches_cache() 2020-01-09 10:19:03 +01:00
nilfs2 nilfs2: convert to SPDX license tags 2018-09-04 16:45:02 -07:00
nls
notify fs: avoid softlockups in s_inodes iterators 2020-01-12 12:17:20 +01:00
ntfs ntfs: mft: remove VLA usage 2018-08-17 16:20:27 -07:00
ocfs2 ocfs2: fix passing zero to 'PTR_ERR' warning 2020-01-04 19:13:16 +01:00
omfs
openpromfs
orangefs orangefs: rate limit the client not running info message 2019-11-24 08:20:57 +01:00
overlayfs ovl: relax WARN_ON() on rename to self 2019-12-17 20:34:51 +01:00
proc mm, thp, proc: report THP eligibility for each vma 2019-12-17 20:35:45 +01:00
pstore pstore/ram: Write new dumps to start of recycled zones 2020-01-09 10:19:00 +01:00
qnx4
qnx6
quota fs: avoid softlockups in s_inodes iterators 2020-01-12 12:17:20 +01:00
ramfs
reiserfs reiserfs: fix extended attributes on the root directory 2019-12-17 20:35:20 +01:00
romfs
squashfs Squashfs: Compute expected length from inode size rather than block length 2018-08-02 09:34:02 -07:00
sysfs Driver core patches for 4.19-rc1 2018-08-18 11:44:53 -07:00
sysv sysv: return 'err' instead of 0 in __sysv_write_inode 2018-12-17 09:24:30 +01:00
tracefs tracefs: Annotate tracefs_ops with __ro_after_init 2018-07-31 11:32:44 -04:00
ubifs ubifs: ubifs_tnc_start_commit: Fix OOB in layout_in_gaps 2020-01-09 10:19:09 +01:00
udf udf: Fix crash during mount 2019-11-20 18:46:04 +01:00
ufs ufs: fix braino in ufs_get_inode_gid() for solaris UFS flavour 2019-05-25 18:23:46 +02:00
xfs xfs: periodically yield scrub threads to the scheduler 2020-01-09 10:19:09 +01:00
aio.c Fix aio_poll() races 2019-05-02 09:58:59 +02:00
anon_inodes.c anon_inode_getfile(): switch to alloc_file_pseudo() 2018-07-12 10:04:27 -04:00
attr.c fs: Fix attr.c kernel-doc 2018-07-03 16:44:45 -04:00
bad_inode.c get rid of 'opened' argument of ->atomic_open() - part 3 2018-07-12 10:04:20 -04:00
binfmt_aout.c
binfmt_elf_fdpic.c treewide: kmalloc() -> kmalloc_array() 2018-06-12 16:19:22 -07:00
binfmt_elf.c binfmt_elf: Do not move brk for INTERP-less ET_EXEC 2019-10-05 13:10:06 +02:00
binfmt_em86.c
binfmt_flat.c fs/binfmt_flat.c: make load_flat_shared_library() work 2019-07-03 13:14:44 +02:00
binfmt_misc.c turn filp_clone_open() into inline wrapper for dentry_open() 2018-07-10 23:29:03 -04:00
binfmt_script.c exec: load_script: Do not exec truncated interpreter path 2019-11-06 13:05:37 +01:00
block_dev.c bdev: Refresh bdev size for disks without partitioning 2020-01-09 10:19:07 +01:00
buffer.c fs: fix guard_bio_eod to check for real EOD errors 2019-04-05 22:33:00 +02:00
char_dev.c chardev: Avoid potential use-after-free in 'chrdev_open()' 2020-01-14 20:06:57 +01:00
compat_binfmt_elf.c
compat_ioctl.c fix compat handling of FICLONERANGE, FIDEDUPERANGE and FS_IOC_FIEMAP 2020-01-09 10:19:07 +01:00
compat.c
coredump.c
d_path.c
dax.c dax: dax_layout_busy_page() should not unmap cow pages 2019-08-16 10:12:52 +02:00
dcache.c dcache: sort the freeing-without-RCU-delay mess for good. 2019-05-25 18:23:26 +02:00
dcookies.c
direct-io.c direct-io: allow direct writes to empty inodes 2019-03-05 17:58:50 +01:00
drop_caches.c fs: avoid softlockups in s_inodes iterators 2020-01-12 12:17:20 +01:00
eventfd.c Revert changes to convert to ->poll_mask() and aio IOCB_CMD_POLL 2018-06-28 10:40:47 -07:00
eventpoll.c fs/epoll: drop ovflist branch prediction 2019-02-12 19:47:19 +01:00
exec.c sched/fair: Don't free p->numa_faults with concurrent readers 2019-08-04 09:30:56 +02:00
fcntl.c signal: Don't send signals to tasks that don't exist 2018-08-15 23:03:20 -05:00
fhandle.c
file_table.c overlayfs update for 4.19 2018-08-21 18:19:09 -07:00
file.c fs/file.c: initialize init_files.resize_wait 2019-04-05 22:32:59 +02:00
filesystems.c
fs_pin.c
fs_struct.c
fs-writeback.c cgroup,writeback: don't switch wbs immediately on dead wbs if the memcg is dead 2019-11-12 19:21:20 +01:00
inode.c fs: avoid softlockups in s_inodes iterators 2020-01-12 12:17:20 +01:00
internal.h acct_on(): don't mess with freeze protection 2019-05-31 06:46:05 -07:00
ioctl.c vfs: fix FIGETBSZ ioctl on an overlayfs file 2018-11-21 09:19:14 +01:00
iomap.c iomap: partially revert 4721a60109 (simulated directio short read on EFAULT) 2019-12-13 08:52:56 +01:00
Kconfig autofs: remove left-over autofs4 stubs 2018-06-11 08:22:34 -07:00
Kconfig.binfmt kconfig: move the "Executable file formats" menu to fs/Kconfig.binfmt 2018-08-02 08:06:55 +09:00
libfs.c Fix the locking in dcache_readdir() and friends 2019-10-17 13:45:35 -07:00
locks.c locks: print unsigned ino in /proc/locks 2020-01-09 10:19:00 +01:00
Makefile autofs: remove left-over autofs4 stubs 2018-06-11 08:22:34 -07:00
mbcache.c treewide: kmalloc() -> kmalloc_array() 2018-06-12 16:19:22 -07:00
mount.h
mpage.c mpage: mpage_readpages() should submit IO as read-ahead 2018-08-17 16:20:29 -07:00
namei.c Revert "vfs: Allow userns root to call mknod on owned filesystems." 2018-12-29 13:37:54 +01:00
namespace.c mnt: fix __detach_mounts infinite loop 2018-11-21 09:19:22 +01:00
no-block.c
nsfs.c dcache: sort the freeing-without-RCU-delay mess for good. 2019-05-25 18:23:26 +02:00
open.c access: avoid the RCU grace period for the temporary subjective credentials 2019-07-31 07:27:11 +02:00
pipe.c fs: prevent page refcount overflow in pipe_buf_get 2019-05-04 09:20:11 +02:00
pnode.c
pnode.h
posix_acl.c
proc_namespace.c
read_write.c vfs: avoid problematic remapping requests into partial EOF block 2019-12-01 09:17:04 +01:00
readdir.c filldir[64]: remove WARN_ON_ONCE() for bad directory entries 2020-01-04 19:13:26 +01:00
select.c Revert changes to convert to ->poll_mask() and aio IOCB_CMD_POLL 2018-06-28 10:40:47 -07:00
seq_file.c seq_file: fix problem when seeking mid-record 2019-08-25 10:47:43 +02:00
signalfd.c Merge branch 'work.compat' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs 2018-06-16 16:21:50 +09:00
splice.c splice: only read in as much information as there is pipe buffer space 2019-12-17 20:35:43 +01:00
stack.c
stat.c
statfs.c vfs: Fix EOVERFLOW testing in put_compat_statfs64 2019-10-11 18:21:39 +02:00
super.c Merge branch 'ida-4.19' of git://git.infradead.org/users/willy/linux-dax 2018-08-26 11:48:42 -07:00
sync.c
timerfd.c Merge branch 'work.aio' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs 2018-08-13 20:56:23 -07:00
userfaultfd.c userfaultfd: require CAP_SYS_PTRACE for UFFD_FEATURE_EVENT_FORK 2020-01-04 19:13:18 +01:00
utimes.c
xattr.c sysfs: Do not return POSIX ACL xattrs via listxattr 2018-09-18 07:30:48 -04:00