xorl %eax, %eax

Linux kernel UNIX Extensions CIFS NULL Pointer Dereference

with one comment

A few days ago Eugene Teo repoerted a vulnerability in Linux kernel’s CIFS code to the linux-cifs-client mailing list. If we have a quick look at fs/cifs/dir.c of 2.6.32 release of the Linux kernel we can read the following.

/* Inode operations in similar order to how they appear in Linux file fs.h */

int
cifs_create(struct inode *inode, struct dentry *direntry, int mode,
                struct nameidata *nd)
      ...
        if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
            (CIFS_UNIX_POSIX_PATH_OPS_CAP &
                        le64_to_cpu(tcon->fsUnixInfo.Capability))) {
                rc = cifs_posix_open(full_path, &newinode, nd->path.mnt,
                                     mode, oflags, &oplock, &fileHandle, xid);
      ...
        return rc;
}

This routine is used to create an CIFS entry and the above code snippet is a check that will ensure ‘tcon->unix_ext’ (this is a Boolean flag for Linux extensions to CIFS protocol) isn’t NULL and then proceed to a capabilities check using session’s stored capabilities (through ‘tcon->ses->capabilities’) as well as the UNIX extension filesystem’s capability (using ‘tcon->fsUnixInfo.Capability’). At last, it will call cifs_posix_open() of fs/cifs/dir.c but as it was noted by Eugene Teo, the provided code in cifs_create() doesn’t perform any checks to ensure that the ‘nameidata’ pointer represented by ‘nd’ variable is a valid pointer. Because of this, if a file is created using UNIX extensions support and that file doesn’t provide any ‘nameidata’ pointer it will lead to ‘nd’ initialized to NULL and the call to cifs_posix_open() shown above will result in a NULL pointer dereference when it’ll attempt to access ‘nd->path.mnt’ to obtain the VFS mount information for that file. Here is how cifs_posix_open() uses the retrieved VFS mount structure (its 3rd argument):

int cifs_posix_open(char *full_path, struct inode **pinode,
                    struct vfsmount *mnt, int mode, int oflags,
                    __u32 *poplock, __u16 *pnetfid, int xid)
{
      ...
        struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb);
      ...
        rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode,
                        pnetfid, presp_data, poplock, full_path,
                        cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
                                        CIFS_MOUNT_MAP_SPECIAL_CHR);
      ...
        cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb);

        /* get new inode and set it up */
        if (*pinode == NULL) {
                *pinode = cifs_iget(mnt->mnt_sb, &fattr);
      ...
        cifs_new_fileinfo(*pinode, *pnetfid, NULL, mnt, oflags);
      ...
        return rc;
}

Even though I haven’t tested anything yet I think that it could lead to exploitable conditions since it’s used by numerous functions that could be used to manipulate data from kernel space assuming that you can map the required pages to avoid the crash. Obviously, the vulnerability was patched simply like this:

 		oflags = FMODE_READ;

-	if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
+	if (nd && tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
 	    (CIFS_UNIX_POSIX_PATH_OPS_CAP &
 			le64_to_cpu(tcon->fsUnixInfo.Capability))) {

Written by xorl

April 5, 2010 at 10:39

Posted in linux, vulnerabilities

One Response

Subscribe to comments with RSS.

  1. Thanks ~ :-)

    clnoe

    August 18, 2010 at 16:39


Leave a comment