This fixes CVE-2016-9602 for all security models. Signed-off-by: Greg Kurz <gr...@kaod.org> --- hw/9pfs/9p-local.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-)
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index a1fff04c3219..1f9239de07e5 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -346,31 +346,48 @@ err: return -1; } +static int readlink_nofollow(FsContext *fs_ctx, const char *path, char *buf, + size_t bufsz) +{ + char *dirpath = local_dirname(path); + char *name = local_basename(path); + int dirfd, ret = -1; + + dirfd = local_opendir_nofollow(fs_ctx, dirpath); + if (dirfd == -1) { + goto out; + } + + ret = readlinkat(dirfd, name, buf, bufsz); + close_preserve_errno(dirfd); +out: + g_free(name); + g_free(dirpath); + return ret; +} + static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, char *buf, size_t bufsz) { - ssize_t tsize = -1; - char *buffer; - char *path = fs_path->data; + ssize_t tsize; if ((fs_ctx->export_flags & V9FS_SM_MAPPED) || (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) { int fd; - buffer = rpath(fs_ctx, path); - fd = open(buffer, O_RDONLY | O_NOFOLLOW); - g_free(buffer); + + fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0); if (fd == -1) { return -1; } do { tsize = read(fd, (void *)buf, bufsz); } while (tsize == -1 && errno == EINTR); - close(fd); + close_preserve_errno(fd); } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || (fs_ctx->export_flags & V9FS_SM_NONE)) { - buffer = rpath(fs_ctx, path); - tsize = readlink(buffer, buf, bufsz); - g_free(buffer); + tsize = readlink_nofollow(fs_ctx, fs_path->data, buf, bufsz); + } else { + g_assert_not_reached(); } return tsize; }