Commit message: 9pfs: xattr: fix memory leak in v9fs_list_xattr

Free 'orig_value' in error path.

Signed-off-by: Li Qiang <liqiang6-s@360.cn>
Signed-off-by: Greg Kurz <groug@kaod.org>
CVE: CVE-2017-8086
CWE: CWE-772
Vulnerability description: Memory leak in the v9fs_list_xattr function in hw/9pfs/9p-xattr.c in QEMU (aka Quick Emulator) allows local guest OS privileged users to cause a denial of service (memory consumption) via vectors involving the orig_value variable.

Project: qemu
File: 9p-xattr.c

static ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
                                     char *list, size_t size)
{
    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
    int ret;

    ret = llistxattr(proc_path, list, size);
    g_free(proc_path);
    return ret;
}

Project: qemu
File: 9p-xattr.c

static ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
                                       const char *name)
{
    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
    int ret;

    ret = lremovexattr(proc_path, name);
    g_free(proc_path);
    return ret;
}

Project: qemu
File: 9p-xattr.c

int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
                         void *value, size_t size, int flags)
{
    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
    int ret;

    ret = lsetxattr(proc_path, name, value, size, flags);
    g_free(proc_path);
    return ret;
}

Project: qemu
File: 9p-xattr.c

static XattrOperations *get_xattr_operations(XattrOperations **h,
                                             const char *name)
{
    XattrOperations *xops;
    for (xops = *(h)++; xops != NULL; xops = *(h)++) {
        if (!strncmp(name, xops->name, strlen(xops->name))) {
            return xops;
        }
    }
    return NULL;
}

Project: qemu
File: 9p-xattr.c

ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
                                const char *name, void *value, size_t size)
{
    char *dirpath = g_path_get_dirname(path);
    char *filename = g_path_get_basename(path);
    int dirfd;
    ssize_t ret = -1;

    dirfd = local_opendir_nofollow(ctx, dirpath);
    if (dirfd == -1) {
        goto out;
    }

    ret = fgetxattrat_nofollow(dirfd, filename, name, value, size);
    close_preserve_errno(dirfd);
out:
    g_free(dirpath);
    g_free(filename);
    return ret;
}

Project: qemu
File: 9p-xattr.c

ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path,
                                   const char *name)
{
    char *dirpath = g_path_get_dirname(path);
    char *filename = g_path_get_basename(path);
    int dirfd;
    ssize_t ret = -1;

    dirfd = local_opendir_nofollow(ctx, dirpath);
    if (dirfd == -1) {
        goto out;
    }

    ret = fremovexattrat_nofollow(dirfd, filename, name);
    close_preserve_errno(dirfd);
out:
    g_free(dirpath);
    g_free(filename);
    return ret;
}

Project: qemu
File: 9p-xattr.c

ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path,
                                const char *name, void *value, size_t size,
                                int flags)
{
    char *dirpath = g_path_get_dirname(path);
    char *filename = g_path_get_basename(path);
    int dirfd;
    ssize_t ret = -1;

    dirfd = local_opendir_nofollow(ctx, dirpath);
    if (dirfd == -1) {
        goto out;
    }

    ret = fsetxattrat_nofollow(dirfd, filename, name, value, size, flags);
    close_preserve_errno(dirfd);
out:
    g_free(dirpath);
    g_free(filename);
    return ret;
}

Project: qemu
File: 9p-xattr.c

ssize_t notsup_getxattr(FsContext *ctx, const char *path, const char *name,
                        void *value, size_t size)
{
    errno = ENOTSUP;
    return -1;
}

Project: qemu
File: 9p-xattr.c

ssize_t notsup_listxattr(FsContext *ctx, const char *path, char *name,
                         void *value, size_t size)
{
    return 0;
}

Project: qemu
File: 9p-xattr.c

int notsup_removexattr(FsContext *ctx, const char *path, const char *name)
{
    errno = ENOTSUP;
    return -1;
}

Project: qemu
File: 9p-xattr.c

int notsup_setxattr(FsContext *ctx, const char *path, const char *name,
                    void *value, size_t size, int flags)
{
    errno = ENOTSUP;
    return -1;
}

Project: qemu
File: 9p-xattr.c

ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name,
                    void *value, size_t size)
{
    return local_getxattr_nofollow(ctx, path, name, value, size);
}

Project: qemu
File: 9p-xattr.c

ssize_t pt_listxattr(FsContext *ctx, const char *path,
                     char *name, void *value, size_t size)
{
    int name_size = strlen(name) + 1;
    if (!value) {
        return name_size;
    }

    if (size < name_size) {
        errno = ERANGE;
        return -1;
    }

    /* no need for strncpy: name_size is strlen(name)+1 */
    memcpy(value, name, name_size);
    return name_size;
}

Project: qemu
File: 9p-xattr.c

int pt_removexattr(FsContext *ctx, const char *path, const char *name)
{
    return local_removexattr_nofollow(ctx, path, name);
}

Project: qemu
File: 9p-xattr.c

int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value,
                size_t size, int flags)
{
    return local_setxattr_nofollow(ctx, path, name, value, size, flags);
}

Project: qemu
File: 9p-xattr.c

ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
                       const char *name, void *value, size_t size)
{
    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
    if (xops) {
        return xops->getxattr(ctx, path, name, value, size);
    }
    errno = EOPNOTSUPP;
    return -1;
}

Project: qemu
File: 9p-xattr.c

int v9fs_remove_xattr(FsContext *ctx,
                      const char *path, const char *name)
{
    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
    if (xops) {
        return xops->removexattr(ctx, path, name);
    }
    errno = EOPNOTSUPP;
    return -1;

}

Project: qemu
File: 9p-xattr.c

int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
                   void *value, size_t size, int flags)
{
    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
    if (xops) {
        return xops->setxattr(ctx, path, name, value, size, flags);
    }
    errno = EOPNOTSUPP;
    return -1;

}