Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 14 Apr 2016 21:28:06 +0000
From:      bugzilla-noreply@freebsd.org
To:        freebsd-bugs@FreeBSD.org
Subject:   [Bug 208808] Heap overflow in nlm system call
Message-ID:  <bug-208808-8@https.bugs.freebsd.org/bugzilla/>

next in thread | raw e-mail | index | archive | help
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D208808

            Bug ID: 208808
           Summary: Heap overflow in nlm system call
           Product: Base System
           Version: 11.0-CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: kern
          Assignee: freebsd-bugs@FreeBSD.org
          Reporter: cturt@hardenedbsd.org

There is a kernel heap overflow in the `nlm_register_services` function,
reachable through the `nlm` system call. Due to the `priv_check` call in
`sys_nlm_syscall`, this is triggerable as root only, so is non critical.

`nlm_register_services` is reachable with user controlled `addr_count`, whe=
re
the following call to `malloc` is performed:

    xprts =3D malloc(addr_count * sizeof(SVCXPRT *), M_NLM, M_WAITOK|M_ZERO=
);

Firstly, since this allocation passes the `M_WAITOK` flag, by specifying la=
rge
enough sizes, a user could cause DoS. However, more interestingly, by
specifying an `addr_count` such that integer overflow occurs during the
multiplication, an undersized buffer would be allocated, leading to heap
overflow later on.

Full code path goes through `sys_nlm_syscall -> nlm_server_main ->
nlm_register_services`, `(sys/nlm/nlm_prot_impl.c)`:

int
sys_nlm_syscall(struct thread *td, struct nlm_syscall_args *uap)
{
        int error;

#if __FreeBSD_version >=3D 700000
        error =3D priv_check(td, PRIV_NFS_LOCKD);
#else
        error =3D suser(td);
#endif
        if (error)
                return (error);

        nlm_debug_level =3D uap->debug_level;
        nlm_grace_threshold =3D time_uptime + uap->grace_period;
        nlm_next_idle_check =3D time_uptime + NLM_IDLE_PERIOD;

        return nlm_server_main(uap->addr_count, uap->addrs);
}

static int
nlm_server_main(int addr_count, char **addrs)
{
        struct thread *td =3D curthread;
        int error;
        SVCPOOL *pool =3D NULL;
        struct sockopt opt;
        int portlow;
#ifdef INET6
        struct sockaddr_in6 sin6;
#endif
        struct sockaddr_in sin;
        my_id id;
        sm_stat smstat;
        struct timeval timo;
        enum clnt_stat stat;
        struct nlm_host *host, *nhost;
        struct nlm_waiting_lock *nw;
        vop_advlock_t *old_nfs_advlock;
        vop_reclaim_t *old_nfs_reclaim;

        if (nlm_is_running !=3D 0) {
                NLM_ERR("NLM: can't start server - "
                    "it appears to be running already\n");
                return (EPERM);
        }

        ...

        error =3D nlm_register_services(pool, addr_count, addrs);

        ...
}

static int
nlm_register_services(SVCPOOL *pool, int addr_count, char **addrs)
{
        static rpcvers_t versions[] =3D {
                NLM_SM, NLM_VERS, NLM_VERSX, NLM_VERS4
        };
        static void (*dispatchers[])(struct svc_req *, SVCXPRT *) =3D {
                nlm_prog_0, nlm_prog_1, nlm_prog_3, nlm_prog_4
        };
        static const int version_count =3D sizeof(versions) /
sizeof(versions[0]);

        SVCXPRT **xprts;
        char netid[16];
        char uaddr[128];
        struct netconfig *nconf;
        int i, j, error;

        if (!addr_count) {
                NLM_ERR("NLM: no service addresses given - can't start
server");
                return (EINVAL);
        }

        xprts =3D malloc(addr_count * sizeof(SVCXPRT *), M_NLM, M_WAITOK|M_=
ZERO);

        ...
}

I propose that there should be a bound check on `addr_count` in
`nlm_register_services`, such as the following:

        if (!addr_count) {
                NLM_ERR("NLM: no service addresses given - can't start
server");
                return (EINVAL);
        }

+       if (addr_count < 0 || addr_count > 256) {
+               NLM_ERR("NLM: too many service addresses given - can't start
server");
+               return (EINVAL);
+       }
+
        xprts =3D malloc(addr_count * sizeof(SVCXPRT *), M_NLM, M_WAITOK|M_=
ZERO);

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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-208808-8>