[Bug 206626] Integer overflow in nfssvc system call

bugzilla-noreply at freebsd.org bugzilla-noreply at freebsd.org
Wed Apr 13 19:29:18 UTC 2016


https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=206626

--- Comment #2 from CTurt <cturt at hardenedbsd.org> ---
It is possible to get panic from this bug by reducing the `nid_namelen` value
to `0xfffffffe`. In this case, the system will panic due to a general
protection fault in `vmem_alloc`. I've updated the PoC code to cause panic by
this method (https://gist.github.com/CTurt/957360482a4dc453f6a4).

The patch for this bug is to add appropriate bound checks to `nfssvc_idname` in
`sys/fs/nfs/nfs_commonsubs.c`:

APPLESTATIC int
nfssvc_idname(struct nfsd_idargs *nidp)
{
        struct nfsusrgrp *nusrp, *usrp, *newusrp;
        struct nfsuserhashhead *hp;
        int i;
        int error = 0;
        u_char *cp;

+       if (nidp->nid_namelen < 0 || nidp->nid_namelen > 128) {
+               error = EINVAL;
+               goto exit;
+       }
+
        ...

Additionally, to better explain this bug, I've decided to go through the full
code path to trigger it:

When supplying the `NFSSVC_IDNAME` flag to the `nfssvc` system call, after
passing the privilege check, the `nfsd_call_nfscommon` function pointer will be
called, which points to `nfssvc_nfscommon`:

https://github.com/freebsd/freebsd/blob/release/10.2.0/sys/nfs/nfs_nfssvc.c#L75

int
sys_nfssvc(struct thread *td, struct nfssvc_args *uap)
{
        int error;

        KASSERT(!mtx_owned(&Giant), ("nfssvc(): called with Giant"));

        AUDIT_ARG_CMD(uap->flag);

        /* Allow anyone to get the stats. */
        if ((uap->flag & ~NFSSVC_GETSTATS) != 0) {
                error = priv_check(td, PRIV_NFS_DAEMON);
                if (error != 0)
                        return (error);
        }
        error = EINVAL;
        if ((uap->flag & (NFSSVC_ADDSOCK | NFSSVC_OLDNFSD | NFSSVC_NFSD)) &&
            ...
        else if ((uap->flag & (NFSSVC_IDNAME | NFSSVC_GETSTATS |
            NFSSVC_GSSDADDPORT | NFSSVC_GSSDADDFIRST | NFSSVC_GSSDDELETEALL |
            NFSSVC_NFSUSERDPORT | NFSSVC_NFSUSERDDELPORT)) &&
            nfsd_call_nfscommon != NULL)
                error = (*nfsd_call_nfscommon)(td, uap);
        ...
        return (error);
}

`nfssvc_nfscommon` then calls `nfssvc_call`:

https://github.com/freebsd/freebsd/blob/release/10.2.0/sys/fs/nfs/nfs_commonport.c#L433

static int
nfssvc_nfscommon(struct thread *td, struct nfssvc_args *uap)
{
        int error;

        error = nfssvc_call(td, uap, td->td_ucred);
        NFSEXITCODE(error);
        return (error);
}

The `nfsd_idargs` struct will then be copied in from userland, and passed to
`nfssvc_idname`:

static int
nfssvc_call(struct thread *p, struct nfssvc_args *uap, struct ucred *cred)
{
        int error = EINVAL;
        struct nfsd_idargs nid;

        if (uap->flag & NFSSVC_IDNAME) {
                error = copyin(uap->argp, (caddr_t)&nid, sizeof (nid));
                if (error)
                        goto out;
                error = nfssvc_idname(&nid);
                goto out;
                ...

In `nfssvc_idname` we have an allocation with `nidp->nid_namelen + 1`, and then
`copyin` with `nidp->nid_namelen`. There were no bound checks on
`nidp->nid_namelen`, so we have `malloc` with completely user controlled size:

https://github.com/freebsd/freebsd/blob/release/10.2.0/sys/fs/nfs/nfs_commonsubs.c#L3093

APPLESTATIC int
nfssvc_idname(struct nfsd_idargs *nidp)
{
        struct nfsusrgrp *nusrp, *usrp, *newusrp;
        struct nfsuserhashhead *hp;
        int i;
        int error = 0;
        u_char *cp;

        if (nidp->nid_flag & NFSID_INITIALIZE) {
            cp = (u_char *)malloc(nidp->nid_namelen + 1,
                M_NFSSTRING, M_WAITOK);
            error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
                nidp->nid_namelen);
                ...

-- 
You are receiving this mail because:
You are the assignee for the bug.


More information about the freebsd-bugs mailing list