BUG: possible NULL pointer dereference in nfs server
Dimitry Andric
dim at FreeBSD.org
Sat Jan 25 16:28:19 UTC 2014
On 25 Jan 2014, at 01:38, Rick Macklem <rmacklem at uoguelph.ca> wrote:
> Roman Divacky wrote:
>> Hi,
>>
>> In nfs_nfsdstate.c:nfsrv_lockctrl() we call
>>
>> getlckret = nfsrv_getlockfh(vp, new_stp->ls_flags, NULL, &nfh, p);
>>
>> then in nfsrv_getlockfh() we, based on the value of flags, might
>> dereference the NULL pointer:
>>
>>
>> nfsrv_getlockfh(vnode_t vp, u_short flags,
>> struct nfslockfile **new_lfpp, fhandle_t *nfhp, NFSPROC_T *p)
>>
>>
>> if (flags & NFSLCK_OPEN) {
>> new_lfp = *new_lfpp;
>> fhp = &new_lfp->lf_fh;
>>
>>
> I took a look and don't see a problem. NFSLCK_OPEN is only set for Opens
> { nfsrvd_open() } and it never calls nfsrv_lockctrl(). nfsrv_lockctrl() is
> always called with other flags in new_stp->ls_flags set, but never NFSLCK_OPEN,
> because nfsrv_lockctrl() is handling byte range lock cases after a file has been
> opened { which means the "else" case for this "if" always applies.
The point is that the compiler cannot always know this, when it inlines
nfsrv_getlockfh(). In the first invocation of nfsrv_getlockfh(), there
is no problem:
712 APPLESTATIC void
713 nfsrv_dumplocks(vnode_t vp, struct nfsd_dumplocks *ldumpp, int maxcnt,
714 NFSPROC_T *p)
715 {
...
726 ret = nfsrv_getlockfh(vp, 0, NULL, &nfh, p);
Here both the 'flags' and 'new_lfpp' arguments are zero, so the part in
nfsrv_getlockfh() where it writes to a NULL pointer can never be
executed, therefore it can be optimized away safely.
In the next invocation, the state of the 'flags' argument is not known
to the compiler, however:
1369 tryagain:
1370 if (new_stp->ls_flags & NFSLCK_LOCK)
1371 MALLOC(other_lop, struct nfslock *, sizeof (struct nfslock),
1372 M_NFSDLOCK, M_WAITOK);
...
1387 getlckret = nfsrv_getlockfh(vp, new_stp->ls_flags, NULL, &nfh, p);
If it inlines this, the result looks approximately like:
1 {
2 fhandle_t *fhp = NULL;
3 struct nfslockfile *new_lfp;
4 int error;
5
6 if (new_stp->ls_flags & NFSLCK_OPEN) {
7 new_lfp = *NULL;
8 fhp = &new_lfp->lf_fh;
9 } else if (&nfh) {
10 fhp = &nfh;
11 } else {
12 panic("nfsrv_getlockfh");
13 }
14 error = nfsvno_getfh(vp, fhp, p);
15 NFSEXITCODE(error);
16 getlckret = error;
17 }
The code in line 7 is the problematic part. Since this is undefined,
the compiler inserts a trap instruction here. I think the problem Roman
encountered is that on sparc64, there is no equivalent to x86's ud2
instruction, so it inserts a call to abort() instead, and that function
is not available in kernel-land.
...
> Sorry, I'm not a compiler guy, so I don't know why a compiler would
> generate a trap instruction, but since new_lfpp is never NULL when
> this is executed, I don't see a problem.
>
> If others feel that this needs to be re-coded, please let me know what
> you think the code should look like? (A test for non-NULL with a panic()
> before it is used?)
>
> Is a trap instruction that never gets executed a problem?
It's better to avoid undefined behavior in any case. Just add a NULL
check, that should be sufficient.
-Dimitry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 203 bytes
Desc: Message signed with OpenPGP using GPGMail
URL: <http://lists.freebsd.org/pipermail/freebsd-fs/attachments/20140125/2984eeb2/attachment.sig>
More information about the freebsd-fs
mailing list