tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Overflow in ptyfs



Hi,
four bytes may overflow in ptyfs_vfsops.c::ptyfs_mount().

        mount(XX, XX, XX, data, data_len)

The kernel will copyin data_len bytes of data into a kernel-allocated buffer.
This kernel buffer and data_len will be given to the appropriate xx_mount()
function, where xx is a fs.

ptyfs_mount() does not handle it correctly:

264     if (*data_len != sizeof *args && *data_len != OSIZE)
265             return EINVAL;

Two sizes are accepted, sizeof(struct ptyfs_args) where ptyfs_args is

        struct ptyfs_args {
                int version;
                gid_t gid;
                mode_t mode;
                int flags;
        };

and OSIZE, the size of the old ptyfs_args structure, which does not have the
last 'flags' field.

278             if (args->version >= PTYFS_ARGSVERSION) {
279                     args->flags = pmnt->pmnt_flags;
280                     *data_len = sizeof *args;
281             } else {
282                     *data_len = OSIZE;
283             }

Here, 'args->flags' is overwritten, depending on the argument version.

But 'data_len' and 'args->version' are user-controlled. Therefore, you can set
'args->version' to PTYFS_ARGSVERSION, and give 'data_len' = OSIZE. On that case,
the kernel will allocate and populate OSIZE bytes, and 'args->flags' will be
overwritten while it actually resides outside the allocated buffer. 4 bytes
overflow.

On NetBSD-current, KMEM_REDZONE will detect it, and will panic. On normal
systems, it won't cause any trouble: once rounded up to a kmem page,
sizeof(struct ptyfs_args) and OSIZE are equal, so the actual allocated size
does not change in both cases. It's not a "real" overflow.

--------------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <fs/ptyfs/ptyfs.h>

#define OSIZE sizeof(struct { int f; gid_t g; mode_t m; })

int main() {
        struct ptyfs_args args;
        int ret;

        args.version = PTYFS_ARGSVERSION;
        ret = mount("ptyfs", "/dev/pts", MNT_GETARGS, &args, OSIZE);
        /* NOTREACHED NetBSD-current (KMEM_REDZONE) */
        printf("RET: %d\n", ret);
}

--------------------------------------------------------------------------------

Index: ptyfs_vfsops.c
===================================================================
RCS file: /cvsroot/src/sys/fs/ptyfs/ptyfs_vfsops.c,v
retrieving revision 1.50
diff -u -r1.50 ptyfs_vfsops.c
--- ptyfs_vfsops.c      16 Apr 2014 18:55:18 -0000      1.50
+++ ptyfs_vfsops.c      11 Aug 2014 06:58:35 -0000
@@ -261,8 +261,10 @@
 
        if (args == NULL)
                return EINVAL;
-       if (*data_len != sizeof *args && *data_len != OSIZE)
-               return EINVAL;
+       if (*data_len != sizeof *args) {
+               if (*data_len != OSIZE || args->version >= PTYFS_ARGSVERSION)
+                       return EINVAL;
+       }
 
        if (UIO_MX & (UIO_MX - 1)) {
                log(LOG_ERR, "ptyfs: invalid directory entry size");


Ok/Comments?





Home | Main Index | Thread Index | Old Index