[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