CVE-2026-80527
Fecha de publicación:
26/08/2026
*** Pendiente de traducción *** In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
ceph: fix hanging __ceph_get_caps() with stale mds_wanted<br />
<br />
A reader can hang forever in __ceph_get_caps() when the client no<br />
longer holds `FILE_RD`, but local cap state still says that the<br />
capability is already wanted (via `mds_wanted`).<br />
<br />
One way to trigger this is through MDS cap revocation. If another<br />
client performs a conflicting operation, the MDS can revoke `FILE_RD`<br />
from the reader; the next read then has to reacquire `FILE_RD`. If<br />
the cap update that should request `FILE_RD` never reaches the MDS<br />
after `cap->mds_wanted` was raised, the reader is left holding only<br />
non-file caps while local `mds_wanted` still includes the file read<br />
caps.<br />
<br />
In that state, try_get_cap_refs() sees `need mds_wanted was` raised, no further request is sent and the<br />
waiter can sleep indefinitely until unrelated cap traffic happens to<br />
wake it up.<br />
<br />
The ordering issue is that `cap->mds_wanted` is updated in<br />
__prep_cap() before the `CEPH_MSG_CLIENT_CAPS message` is actually<br />
queued for send. That makes one field serve two different meanings at<br />
once: what this client wants, and what the client believes the MDS<br />
already knows it wants.<br />
<br />
A proper fix would be to split those states and track whether a cap<br />
update is actually in flight or has been observed by the MDS.<br />
However, simply moving the `cap->mds_wanted assignment` later would<br />
not be sufficient: queueing the message in the messenger does not<br />
guarantee that the MDS processed that specific wanted set, and<br />
reconnect or message loss can still invalidate that assumption.<br />
Fixing that properly would require a larger rework of the cap state<br />
machine.<br />
<br />
To allow simpler backports to stable kernels, this patch implements a<br />
simpler workaround:<br />
<br />
- stop waiting forever in __ceph_get_caps(); after a bounded wait,<br />
fall back to the renew path<br />
<br />
- make ceph_renew_caps() issue a synchronous `OPEN` request whenever<br />
the inode still does not actually hold the wanted caps, instead of<br />
only calling ceph_check_caps()<br />
<br />
The extra issued-vs-wanted check in ceph_renew_caps() is necessary<br />
because the previous test only checked whether the inode still had any<br />
real caps at all. That is not enough after revocation: the client can<br />
still hold something like `pLs` and yet be missing `FILE_RD`<br />
completely. In that case, falling back to ceph_check_caps() is not<br />
sufficient, because it still trusts `cap->mds_wanted` and may resend<br />
nothing. By requiring `(issued & wanted) == wanted` before taking the<br />
asynchronous path, the code only uses ceph_check_caps() when the<br />
`wanted caps` are already actually issued. Otherwise, it sends the<br />
synchronous `OPEN` renew.<br />
<br />
This preserves the existing asynchronous fast path when the wanted<br />
caps are already issued, avoids changing cap-state semantics, and<br />
fixes the hang by guaranteeing that a stalled waiter eventually<br />
retries through a path that does not rely on the stale `mds_wanted`<br />
state.<br />
<br />
[ idryomov: move CEPH_GET_CAPS_WAIT_TIMEOUT from libceph.h to<br />
mds_client.h, formatting ]
Gravedad CVSS v3.1: ALTA
Última modificación:
27/08/2026