kernel svc_rpc_gss_update_seq missing 2017 Coverity fix + TOCTOU with check_replay
Stanislav Fort <[email protected]>
| Newsgroups | gmane.os.freebsd.devel.net |
|---|---|
| Message-ID | <CA+uRpKTejNT23zxj2dXE3BdY14R=rx-YV_g4oiwRoq1UNVKv2Q@mail.gmail.com> |
Hi there,
I think there might be two bugs in sys/rpc/rpcsec_gss/svc_rpcsec_gss.c in
svc_rpc_gss_update_seq():
1. Missing port of lib/librpcsec_gss fix from 11bc2c1ca77e
The userspace lib/librpcsec_gss/svc_rpcsec_gss.c was fixed in 2017 (commit
11bc2c1ca77e, Coverity CID 1198859) to change "while (offset > 32)" to
"while (offset >= 32)" and guard the subsequent bit-shift loop with "if
(offset > 0)". The kernel copy still has the old pattern:
while (offset > 32) { /* should be >= 32 */
/* ... word shift ... */
offset -= 32;
}
/* missing: if (offset > 0) { */
carry = 0;
for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
newcarry = client->cl_seqmask[i] >> (32 - offset);
client->cl_seqmask[i] =
(client->cl_seqmask[i] << offset) | carry; /* UB when
offset==32 */
carry = newcarry;
}
When offset is exactly 32, the while loop doesn't execute and the shift
loop does << 32 on a uint32_t, which is undefined behavior(?). The fix is
to match what lib/librpcsec_gss already does: use >= 32 and wrap the shift
loop in if (offset > 0).
2. TOCTOU between check_replay and update_seq
In svc_rpc_gss(), svc_rpc_gss_check_replay() and svc_rpc_gss_update_seq()
each independently acquire and release cl_lock. Between the two calls,
svc_rpc_gss_validate() and svc_rpc_gss_nextverf() run without the lock
held. Since svc_rpc_gss_find_client() matches clients by opaque handle and
doesn't bind them to a specific transport, two connections presenting the
same GSS handle can drive concurrent threads through svc_rpc_gss() on the
same client struct.
If thread A passes check_replay for a low sequence number, then thread B
advances cl_seqlast significantly before thread A enters update_seq, the
else branch in update_seq computes:
offset = client->cl_seqlast - seq;
word = offset / 32;
bit = offset % 32;
client->cl_seqmask[word] |= (1 << bit);
Word can exceed SVC_RPC_GSS_SEQWINDOW/32 - 1 (i.e., >= 4), causing an OOB
write to cl_seqmask. This requires authenticated access (GSS MIC
verification still gates both paths) and winning a race, so practical
impact is limited.
A simple fix would be to hold cl_lock across both check and update, or to
re-validate offset < SVC_RPC_GSS_SEQWINDOW inside update_seq before writing.
Let me know what you think.
Best wishes,
Stanislav Fort
Aisle Research