xorl %eax, %eax

NetBSD-SA2010-001: mount(2) Module Autoloading Race Condition

leave a comment »

A few hours ago NetBSD project published its first security advisory for 2010 which is this one!
Here is the code as seen in NetBSD 5.0’s src/sys/kern/vfs_syscalls.c …

static int
mount_get_vfsops(const char *fstype, struct vfsops **vfsops)
{
        char fstypename[sizeof(((struct statvfs *)NULL)->f_fstypename)];
        int error;
 
        /* Copy file-system type from userspace.  */
        error = copyinstr(fstype, fstypename, sizeof(fstypename), NULL);
     ...
        /* If we can autoload a vfs module, try again */
        mutex_enter(&module_lock);
        (void)module_autoload(fstype, MODULE_CLASS_VFS);
        mutex_exit(&module_lock);
     ...
}

So, the filesystem string is copied directly from the userspace using copyinstr() and it’s stored in ‘fstypename’ variable. Then, this code will invoke module_autoload() to load that module in case it’s not already loaded. However, as you can see… module_autoload() will use ‘fstype’ instead of the ‘fstypename’ which is a pointer to a userspace address. Let’s move to module_autoload() at kern/kern_module.c…

/*
 * module_autoload:
 *
 *      Load a single module from the file system, system initiated.
 */
int
module_autoload(const char *filename, modclass_t class)
{
        int error;
 
        KASSERT(mutex_owned(&module_lock));
 
        /* Authorize. */
        error = kauth_authorize_system(kauth_cred_get(), KAUTH_SYSTEM_MODULE,
            0, (void *)(uintptr_t)MODCTL_LOAD, (void *)(uintptr_t)1, NULL);
        if (error != 0) {
               return error;
        }
 
        return module_do_load(filename, false, 0, NULL, NULL, class, true);
}

So… it calls module_do_load() which will in turn call kobj_load_file() from kern/subr_kobj.c which will attemp to execute the following:

/*
 * kobj_load_file:
 *
 *      Load an object located in the file system.
 */
int
kobj_load_file(kobj_t *kop, const char *filename, const char *base,
               bool autoload)
      ...
                 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename);
      ...
}

and a quick look at is telling us that this is:

/*
 * Initialization of an nameidata structure.
 */
#define NDINIT(ndp, op, flags, segflg, namep) { \
        (ndp)->ni_cnd.cn_nameiop = op; \
        (ndp)->ni_cnd.cn_flags = flags; \
        (ndp)->ni_segflg = segflg; \
        (ndp)->ni_dirp = namep; \
        (ndp)->ni_cnd.cn_cred = kauth_cred_get(); \
}
#endif

Assuming that the user has unmapped the userspace location between the initial call to module_autoload() and the above code, this will result in an invalid pointer access condition.
To fix this, the following patch was used:

 	mutex_enter(&module_lock);
-	(void)module_autoload(fstype, MODULE_CLASS_VFS);
+	(void)module_autoload(fstypename, MODULE_CLASS_VFS);
 	mutex_exit(&module_lock);

This vulnerability was discovered and reported by Martin Husemann.
To trigger it, as the advisory states you only have to call mount(2) with its filesystem argument pointing to a space that will be unmapped before the module_autoload() call in the above snippet.

Written by xorl

January 14, 2010 at 05:06

Posted in bugs, netbsd

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s