[Question] nfsd: possible reordering between nf->nf_file assignment and NFSD_FILE_PENDING clearing?
From: Li Lingfeng
Date: Thu Sep 18 2025 - 09:58:04 EST
Recently, we encountered a null pointer dereference on a relatively old
5.10 kernel that does not include commit c4c649ab413ba ("NFSD: Convert
filecache to rhltable"), which exhibited the same behavior as described
in [1]. I was wondering if it might be caused by the reordering between
the assignment of nf->nf_file and the clearing of NFSD_FILE_PENDING.
Just to mention, I don't believe the analysis in [1] is entirely accurate,
since hlist_add_head_rcu includes a write barrier.
We haven't encountered this issue on newer kernel versions, but the
assignment of nf->nf_file and the clearing of NFSD_FILE_PENDING appear
consistent across different versions.
Our expected outcome should be like this:
T1 T2
nfsd_read
nfsd_file_acquire_gc
nfsd_file_do_acquire
nfsd_file_lookup_locked
// get nfsd_file from nfsd_file_rhltable
nfsd_read
nfsd_file_acquire_gc
nfsd_file_do_acquire
nfsd_file_alloc
nf->nf_flags // set NFSD_FILE_PENDING
rhltable_insert // insert to nfsd_file_rhltable
nf->nf_file = file // set nf_file
wait_on_bit
// wait NFSD_FILE_PENDING to be cleared
clear_and_wake_up_bit // clear NFSD_FILE_PENDING
// get file after being awakened
file = nf->nf_file
Or like this:
T1 T2
nfsd_read
nfsd_file_acquire_gc
nfsd_file_do_acquire
nfsd_file_lookup_locked
// get nfsd_file from nfsd_file_rhltable
nfsd_read
nfsd_file_acquire_gc
nfsd_file_do_acquire
nfsd_file_alloc
nf->nf_flags // set NFSD_FILE_PENDING
rhltable_insert // insert to nfsd_file_rhltable
nf->nf_file = file // set nf_file
clear_and_wake_up_bit // clear NFSD_FILE_PENDING
// get file directly
file = nf->nf_file
But is it possible that due to reordering, it ends up like this:
T1 T2
nfsd_read
nfsd_file_acquire_gc
nfsd_file_do_acquire
nfsd_file_lookup_locked
// get nfsd_file from nfsd_file_rhltable
nfsd_read
nfsd_file_acquire_gc
nfsd_file_do_acquire
nfsd_file_alloc
nf->nf_flags // set NFSD_FILE_PENDING
rhltable_insert // insert to nfsd_file_rhltable
clear_and_wake_up_bit // clear NFSD_FILE_PENDING
// get file directly
file = nf->nf_file
nf->nf_file = file // set nf_file
// Null dereference due to uninitialized file pointer.
[1]: https://lore.kernel.org/all/20230818065507.1280625-1-haydenw.kernel@xxxxxxxxx/
Any suggestion will be appreciated.
Thanks,
Lingfeng.