Skip to content

Commit 894022e

Browse files
vivierjasowang
authored andcommitted
net: check if the file descriptor is valid before using it
qemu_set_nonblock() checks that the file descriptor can be used and, if not, crashes QEMU. An assert() is used for that. The use of assert() is used to detect programming error and the coredump will allow to debug the problem. But in the case of the tap device, this assert() can be triggered by a misconfiguration by the user. At startup, it's not a real problem, but it can also happen during the hot-plug of a new device, and here it's a problem because we can crash a perfectly healthy system. For instance: # ip link add link virbr0 name macvtap0 type macvtap mode bridge # ip link set macvtap0 up # TAP=/dev/tap$(ip -o link show macvtap0 | cut -d: -f1) # qemu-system-x86_64 -machine q35 -device pcie-root-port,id=pcie-root-port-0 -monitor stdio 9<> $TAP (qemu) netdev_add type=tap,id=hostnet0,vhost=on,fd=9 (qemu) device_add driver=virtio-net-pci,netdev=hostnet0,id=net0,bus=pcie-root-port-0 (qemu) device_del net0 (qemu) netdev_del hostnet0 (qemu) netdev_add type=tap,id=hostnet1,vhost=on,fd=9 qemu-system-x86_64: .../util/oslib-posix.c:247: qemu_set_nonblock: Assertion `f != -1' failed. Aborted (core dumped) To avoid that, add a function, qemu_try_set_nonblock(), that allows to report the problem without crashing. In the same way, we also update the function for vhostfd in net_init_tap_one() and for fd in net_init_socket() (both descriptors are provided by the user and can be wrong). Signed-off-by: Laurent Vivier <[email protected]> Reviewed-by: Philippe Mathieu-Daudé <[email protected]> Signed-off-by: Jason Wang <[email protected]>
1 parent 2b28a7e commit 894022e

File tree

5 files changed

+79
-39
lines changed

5 files changed

+79
-39
lines changed

include/qemu/sockets.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
1818
int socket_set_cork(int fd, int v);
1919
int socket_set_nodelay(int fd);
2020
void qemu_set_block(int fd);
21+
int qemu_try_set_nonblock(int fd);
2122
void qemu_set_nonblock(int fd);
2223
int socket_set_fast_reuse(int fd);
2324

net/socket.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -725,13 +725,18 @@ int net_init_socket(const Netdev *netdev, const char *name,
725725
}
726726

727727
if (sock->has_fd) {
728-
int fd;
728+
int fd, ret;
729729

730730
fd = monitor_fd_param(cur_mon, sock->fd, errp);
731731
if (fd == -1) {
732732
return -1;
733733
}
734-
qemu_set_nonblock(fd);
734+
ret = qemu_try_set_nonblock(fd);
735+
if (ret < 0) {
736+
error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
737+
name, fd);
738+
return -1;
739+
}
735740
if (!net_socket_fd_init(peer, "socket", name, fd, 1, sock->mcast,
736741
errp)) {
737742
return -1;

net/tap.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,8 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
690690
}
691691

692692
if (vhostfdname) {
693+
int ret;
694+
693695
vhostfd = monitor_fd_param(cur_mon, vhostfdname, &err);
694696
if (vhostfd == -1) {
695697
if (tap->has_vhostforce && tap->vhostforce) {
@@ -699,7 +701,12 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
699701
}
700702
return;
701703
}
702-
qemu_set_nonblock(vhostfd);
704+
ret = qemu_try_set_nonblock(vhostfd);
705+
if (ret < 0) {
706+
error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
707+
name, fd);
708+
return;
709+
}
703710
} else {
704711
vhostfd = open("/dev/vhost-net", O_RDWR);
705712
if (vhostfd < 0) {
@@ -767,6 +774,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
767774
Error *err = NULL;
768775
const char *vhostfdname;
769776
char ifname[128];
777+
int ret = 0;
770778

771779
assert(netdev->type == NET_CLIENT_DRIVER_TAP);
772780
tap = &netdev->u.tap;
@@ -795,7 +803,12 @@ int net_init_tap(const Netdev *netdev, const char *name,
795803
return -1;
796804
}
797805

798-
qemu_set_nonblock(fd);
806+
ret = qemu_try_set_nonblock(fd);
807+
if (ret < 0) {
808+
error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
809+
name, fd);
810+
return -1;
811+
}
799812

800813
vnet_hdr = tap_probe_vnet_hdr(fd);
801814

@@ -810,7 +823,6 @@ int net_init_tap(const Netdev *netdev, const char *name,
810823
char **fds;
811824
char **vhost_fds;
812825
int nfds = 0, nvhosts = 0;
813-
int ret = 0;
814826

815827
if (tap->has_ifname || tap->has_script || tap->has_downscript ||
816828
tap->has_vnet_hdr || tap->has_helper || tap->has_queues ||
@@ -842,7 +854,12 @@ int net_init_tap(const Netdev *netdev, const char *name,
842854
goto free_fail;
843855
}
844856

845-
qemu_set_nonblock(fd);
857+
ret = qemu_try_set_nonblock(fd);
858+
if (ret < 0) {
859+
error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
860+
name, fd);
861+
goto free_fail;
862+
}
846863

847864
if (i == 0) {
848865
vnet_hdr = tap_probe_vnet_hdr(fd);

util/oslib-posix.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -260,25 +260,35 @@ void qemu_set_block(int fd)
260260
assert(f != -1);
261261
}
262262

263-
void qemu_set_nonblock(int fd)
263+
int qemu_try_set_nonblock(int fd)
264264
{
265265
int f;
266266
f = fcntl(fd, F_GETFL);
267-
assert(f != -1);
268-
f = fcntl(fd, F_SETFL, f | O_NONBLOCK);
269-
#ifdef __OpenBSD__
270267
if (f == -1) {
268+
return -errno;
269+
}
270+
if (fcntl(fd, F_SETFL, f | O_NONBLOCK) == -1) {
271+
#ifdef __OpenBSD__
271272
/*
272273
* Previous to OpenBSD 6.3, fcntl(F_SETFL) is not permitted on
273274
* memory devices and sets errno to ENODEV.
274275
* It's OK if we fail to set O_NONBLOCK on devices like /dev/null,
275276
* because they will never block anyway.
276277
*/
277-
assert(errno == ENODEV);
278-
}
279-
#else
280-
assert(f != -1);
278+
if (errno == ENODEV) {
279+
return 0;
280+
}
281281
#endif
282+
return -errno;
283+
}
284+
return 0;
285+
}
286+
287+
void qemu_set_nonblock(int fd)
288+
{
289+
int f;
290+
f = qemu_try_set_nonblock(fd);
291+
assert(f == 0);
282292
}
283293

284294
int socket_set_fast_reuse(int fd)

util/oslib-win32.c

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -132,31 +132,6 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
132132
}
133133
#endif /* CONFIG_LOCALTIME_R */
134134

135-
void qemu_set_block(int fd)
136-
{
137-
unsigned long opt = 0;
138-
WSAEventSelect(fd, NULL, 0);
139-
ioctlsocket(fd, FIONBIO, &opt);
140-
}
141-
142-
void qemu_set_nonblock(int fd)
143-
{
144-
unsigned long opt = 1;
145-
ioctlsocket(fd, FIONBIO, &opt);
146-
qemu_fd_register(fd);
147-
}
148-
149-
int socket_set_fast_reuse(int fd)
150-
{
151-
/* Enabling the reuse of an endpoint that was used by a socket still in
152-
* TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows
153-
* fast reuse is the default and SO_REUSEADDR does strange things. So we
154-
* don't have to do anything here. More info can be found at:
155-
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */
156-
return 0;
157-
}
158-
159-
160135
static int socket_error(void)
161136
{
162137
switch (WSAGetLastError()) {
@@ -233,6 +208,38 @@ static int socket_error(void)
233208
}
234209
}
235210

211+
void qemu_set_block(int fd)
212+
{
213+
unsigned long opt = 0;
214+
WSAEventSelect(fd, NULL, 0);
215+
ioctlsocket(fd, FIONBIO, &opt);
216+
}
217+
218+
int qemu_try_set_nonblock(int fd)
219+
{
220+
unsigned long opt = 1;
221+
if (ioctlsocket(fd, FIONBIO, &opt) != NO_ERROR) {
222+
return -socket_error();
223+
}
224+
qemu_fd_register(fd);
225+
return 0;
226+
}
227+
228+
void qemu_set_nonblock(int fd)
229+
{
230+
(void)qemu_try_set_nonblock(fd);
231+
}
232+
233+
int socket_set_fast_reuse(int fd)
234+
{
235+
/* Enabling the reuse of an endpoint that was used by a socket still in
236+
* TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows
237+
* fast reuse is the default and SO_REUSEADDR does strange things. So we
238+
* don't have to do anything here. More info can be found at:
239+
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */
240+
return 0;
241+
}
242+
236243
int inet_aton(const char *cp, struct in_addr *ia)
237244
{
238245
uint32_t addr = inet_addr(cp);

0 commit comments

Comments
 (0)