In the Linux kernel, the following vulnerability has been resolved:
smb: client: avoid leaking refcount in cifs_queue_oplock_break()
cifs_queue_oplock_break() unconditionally takes a reference on the
target file before queueing cifs_oplock_break(). Only that work item
decreases the reference counter again.
If another oplock break arrives while that work is still queued,
queue_work() will return false and not queue this second work item. As a
result, we will never reach the point to drop the file reference again
and are leaking this reference. This can be triggered when interacting
with a slow-responding server.
As a result, later unmount operations for this file system will fail with
BUG: Dentry ... still in use (1) [unmount of cifs cifs]
VFS: Busy inodes after unmount of cifs (cifs)
kernel BUG at fs/super.c:777!
Fix this by only incrementing the reference count if the work has been
queued successfully. Taking it after queue_work() is safe because all
three callers hold tcon->open_file_lock across the call and
_cifsFileInfo_put() decrements under that same lock, so a worker that
starts the handler in the window cannot drop the reference before it has
been taken.
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CVSS Score: 7.5
AV:N - Unsolicited SMB2 OPLOCK_BREAK and StructureSize-44 lease-break PDUs (or SMB1 LOCKING_ANDX with LOCKING_ANDX_OPLOCK_RELEASE) arrive on the CIFS client TCP session; after smb2_find_mid()/cifs_find_mid() miss, cifs_demultiplex_thread() calls smb2_is_valid_oplock_break()/smb2_tcon_has_lease()/is_valid_oplock_break(), which invoke the patched cifs_queue_oplock_break() on the FID/lease-matched cifsFileInfo.
AC:L - A malicious server that issued the PersistentFid/VolatileFid or lease key on CREATE sends a second matching break while cfile->oplock_break is still pending on cifsoplockd_wq, so queue_work() returns false after the extra cifsFileInfo_get(); two back-to-back unsolicited PDUs are processed on the same demux thread before the worker runs, which the attacker creates.
PR:N - The attacker is the SMB server (or a MITM on an unsigned session) and holds no account or capability on the client; oplock/lease breaks are unsolicited notifications handled when find_mid() returns NULL, with no client-side session or share-ACL check on that PDU.
UI:N - On an already-mounted CIFS/SMB3 share the server sends the extra SMB2_OPLOCK_BREAK/lease-break for an open FID it already granted, with no further victim mount or open at attack time; the later umount that hits the busy-dentry BUG is autofs idle expiry, container teardown, or shutdown.
S:U - The leaked cifsFileInfo ref and the generic_shutdown_super() busy-inode BUG stay inside the client kernel's VFS/CIFS objects on the same host; this is not a VM escape, IOMMU bypass, or sandbox boundary crossing.
C:N - The defect only increments cifsFileInfo.count in cifs_queue_oplock_break() when queue_work() fails; the extra ref pins cfile->dentry instead of freeing it, so there is no OOB read, UAF, or kernel data returned to the server or to userspace.
I:N - cifs_oplock_break() still _cifsFileInfo_put()s once per successfully queued work item, and the leaked get is never a write through a freed object, double-free, or control-flow primitive; count is only incremented.
A:H - Each leaked get keeps the dentry from cifs_new_fileinfo()'s dget() alive, so later unmount hits umount_check()'s "Dentry still in use" WARN and generic_shutdown_super()'s CHECK_DATA_CORRUPTION("VFS: Busy inodes after unmount of cifs"), which BUG()s under CONFIG_BUG_ON_DATA_CORRUPTION as the fix commit reports at fs/super.c.
| Attack Vector |
Network |
Scope |
Unchanged |
| Attack Complexity |
Low |
Confidentiality Impact |
None |
| Privileges Required |
None |
Integrity Impact |
None |
| User Interaction |
None |
Availability Impact |
High |
AV:N - Unsolicited SMB2 OPLOCK_BREAK and StructureSize-44 lease-break PDUs (or SMB1 LOCKING_ANDX with LOCKING_ANDX_OPLOCK_RELEASE) arrive on the CIFS client TCP session; after smb2_find_mid()/cifs_find_mid() miss, cifs_demultiplex_thread() calls smb2_is_valid_oplock_break()/smb2_tcon_has_lease()/is_valid_oplock_break(), which invoke the patched cifs_queue_oplock_break() on the FID/lease-matched cifsFileInfo.
AC:L - A malicious server that issued the PersistentFid/VolatileFid or lease key on CREATE sends a second matching break while cfile->oplock_break is still pending on cifsoplockd_wq, so queue_work() returns false after the extra cifsFileInfo_get(); two back-to-back unsolicited PDUs are processed on the same demux thread before the worker runs, which the attacker creates.
PR:N - The attacker is the SMB server (or a MITM on an unsigned session) and holds no account or capability on the client; oplock/lease breaks are unsolicited notifications handled when find_mid() returns NULL, with no client-side session or share-ACL check on that PDU.
UI:N - On an already-mounted CIFS/SMB3 share the server sends the extra SMB2_OPLOCK_BREAK/lease-break for an open FID it already granted, with no further victim mount or open at attack time; the later umount that hits the busy-dentry BUG is autofs idle expiry, container teardown, or shutdown.
S:U - The leaked cifsFileInfo ref and the generic_shutdown_super() busy-inode BUG stay inside the client kernel's VFS/CIFS objects on the same host; this is not a VM escape, IOMMU bypass, or sandbox boundary crossing.
C:N - The defect only increments cifsFileInfo.count in cifs_queue_oplock_break() when queue_work() fails; the extra ref pins cfile->dentry instead of freeing it, so there is no OOB read, UAF, or kernel data returned to the server or to userspace.
I:N - cifs_oplock_break() still _cifsFileInfo_put()s once per successfully queued work item, and the leaked get is never a write through a freed object, double-free, or control-flow primitive; count is only incremented.
A:H - Each leaked get keeps the dentry from cifs_new_fileinfo()'s dget() alive, so later unmount hits umount_check()'s "Dentry still in use" WARN and generic_shutdown_super()'s CHECK_DATA_CORRUPTION("VFS: Busy inodes after unmount of cifs"), which BUG()s under CONFIG_BUG_ON_DATA_CORRUPTION as the fix commit reports at fs/super.c.
CVSS 3.1