Skip to content

Commit 548ec08

Browse files
author
J. Bruce Fields
committed
nfsd: fix use-after-free due to delegation race
A delegation break could arrive as soon as we've called vfs_setlease. A delegation break runs a callback which immediately (in nfsd4_cb_recall_prepare) adds the delegation to del_recall_lru. If we then exit nfs4_set_delegation without hashing the delegation, it will be freed as soon as the callback is done with it, without ever being removed from del_recall_lru. Symptoms show up later as use-after-free or list corruption warnings, usually in the laundromat thread. I suspect aba2072 "nfsd: grant read delegations to clients holding writes" made this bug easier to hit, but I looked as far back as v3.0 and it looks to me it already had the same problem. So I'm not sure where the bug was introduced; it may have been there from the beginning. Cc: [email protected] Signed-off-by: J. Bruce Fields <[email protected]>
1 parent b10252c commit 548ec08

File tree

1 file changed

+7
-2
lines changed

1 file changed

+7
-2
lines changed

fs/nfsd/nfs4state.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,14 +1207,19 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
12071207
return 0;
12081208
}
12091209

1210+
static bool delegation_hashed(struct nfs4_delegation *dp)
1211+
{
1212+
return !(list_empty(&dp->dl_perfile));
1213+
}
1214+
12101215
static bool
12111216
unhash_delegation_locked(struct nfs4_delegation *dp)
12121217
{
12131218
struct nfs4_file *fp = dp->dl_stid.sc_file;
12141219

12151220
lockdep_assert_held(&state_lock);
12161221

1217-
if (list_empty(&dp->dl_perfile))
1222+
if (!delegation_hashed(dp))
12181223
return false;
12191224

12201225
dp->dl_stid.sc_type = NFS4_CLOSED_DELEG_STID;
@@ -4598,7 +4603,7 @@ static void nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
45984603
* queued for a lease break. Don't queue it again.
45994604
*/
46004605
spin_lock(&state_lock);
4601-
if (dp->dl_time == 0) {
4606+
if (delegation_hashed(dp) && dp->dl_time == 0) {
46024607
dp->dl_time = ktime_get_boottime_seconds();
46034608
list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
46044609
}

0 commit comments

Comments
 (0)