Skip to content

Commit f6b0de5

Browse files
committed
9pfs: prevent opening special files (CVE-2023-2861)
The 9p protocol does not specifically define how server shall behave when client tries to open a special file, however from security POV it does make sense for 9p server to prohibit opening any special file on host side in general. A sane Linux 9p client for instance would never attempt to open a special file on host side, it would always handle those exclusively on its guest side. A malicious client however could potentially escape from the exported 9p tree by creating and opening a device file on host side. With QEMU this could only be exploited in the following unsafe setups: - Running QEMU binary as root AND 9p 'local' fs driver AND 'passthrough' security model. or - Using 9p 'proxy' fs driver (which is running its helper daemon as root). These setups were already discouraged for safety reasons before, however for obvious reasons we are now tightening behaviour on this. Fixes: CVE-2023-2861 Reported-by: Yanwu Shen <[email protected]> Reported-by: Jietao Xiao <[email protected]> Reported-by: Jinku Li <[email protected]> Reported-by: Wenbo Shen <[email protected]> Signed-off-by: Christian Schoenebeck <[email protected]> Reviewed-by: Greg Kurz <[email protected]> Reviewed-by: Michael Tokarev <[email protected]> Message-Id: <[email protected]>
1 parent 45ae979 commit f6b0de5

File tree

2 files changed

+64
-2
lines changed

2 files changed

+64
-2
lines changed

fsdev/virtfs-proxy-helper.c

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "qemu/xattr.h"
2727
#include "9p-iov-marshal.h"
2828
#include "hw/9pfs/9p-proxy.h"
29+
#include "hw/9pfs/9p-util.h"
2930
#include "fsdev/9p-iov-marshal.h"
3031

3132
#define PROGNAME "virtfs-proxy-helper"
@@ -338,6 +339,28 @@ static void resetugid(int suid, int sgid)
338339
}
339340
}
340341

342+
/*
343+
* Open regular file or directory. Attempts to open any special file are
344+
* rejected.
345+
*
346+
* returns file descriptor or -1 on error
347+
*/
348+
static int open_regular(const char *pathname, int flags, mode_t mode)
349+
{
350+
int fd;
351+
352+
fd = open(pathname, flags, mode);
353+
if (fd < 0) {
354+
return fd;
355+
}
356+
357+
if (close_if_special_file(fd) < 0) {
358+
return -1;
359+
}
360+
361+
return fd;
362+
}
363+
341364
/*
342365
* send response in two parts
343366
* 1) ProxyHeader
@@ -682,7 +705,7 @@ static int do_create(struct iovec *iovec)
682705
if (ret < 0) {
683706
goto unmarshal_err_out;
684707
}
685-
ret = open(path.data, flags, mode);
708+
ret = open_regular(path.data, flags, mode);
686709
if (ret < 0) {
687710
ret = -errno;
688711
}
@@ -707,7 +730,7 @@ static int do_open(struct iovec *iovec)
707730
if (ret < 0) {
708731
goto err_out;
709732
}
710-
ret = open(path.data, flags);
733+
ret = open_regular(path.data, flags, 0);
711734
if (ret < 0) {
712735
ret = -errno;
713736
}

hw/9pfs/9p-util.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#ifndef QEMU_9P_UTIL_H
1414
#define QEMU_9P_UTIL_H
1515

16+
#include "qemu/error-report.h"
17+
1618
#ifdef O_PATH
1719
#define O_PATH_9P_UTIL O_PATH
1820
#else
@@ -95,6 +97,7 @@ static inline int errno_to_dotl(int err) {
9597
#endif
9698

9799
#define qemu_openat openat
100+
#define qemu_fstat fstat
98101
#define qemu_fstatat fstatat
99102
#define qemu_mkdirat mkdirat
100103
#define qemu_renameat renameat
@@ -108,6 +111,38 @@ static inline void close_preserve_errno(int fd)
108111
errno = serrno;
109112
}
110113

114+
/**
115+
* close_if_special_file() - Close @fd if neither regular file nor directory.
116+
*
117+
* @fd: file descriptor of open file
118+
* Return: 0 on regular file or directory, -1 otherwise
119+
*
120+
* CVE-2023-2861: Prohibit opening any special file directly on host
121+
* (especially device files), as a compromised client could potentially gain
122+
* access outside exported tree under certain, unsafe setups. We expect
123+
* client to handle I/O on special files exclusively on guest side.
124+
*/
125+
static inline int close_if_special_file(int fd)
126+
{
127+
struct stat stbuf;
128+
129+
if (qemu_fstat(fd, &stbuf) < 0) {
130+
close_preserve_errno(fd);
131+
return -1;
132+
}
133+
if (!S_ISREG(stbuf.st_mode) && !S_ISDIR(stbuf.st_mode)) {
134+
error_report_once(
135+
"9p: broken or compromised client detected; attempt to open "
136+
"special file (i.e. neither regular file, nor directory)"
137+
);
138+
close(fd);
139+
errno = ENXIO;
140+
return -1;
141+
}
142+
143+
return 0;
144+
}
145+
111146
static inline int openat_dir(int dirfd, const char *name)
112147
{
113148
return qemu_openat(dirfd, name,
@@ -142,6 +177,10 @@ static inline int openat_file(int dirfd, const char *name, int flags,
142177
return -1;
143178
}
144179

180+
if (close_if_special_file(fd) < 0) {
181+
return -1;
182+
}
183+
145184
serrno = errno;
146185
/* O_NONBLOCK was only needed to open the file. Let's drop it. We don't
147186
* do that with O_PATH since fcntl(F_SETFL) isn't supported, and openat()

0 commit comments

Comments
 (0)