Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vhost: fix error handling in RESET_OWNER ioctl

RESET_OWNER ioctl would leave the fd in a bad state if
memory allocation failed: device is stopped
but owner is not reset. Make state changes
after allocating memory, such that a failed
ioctl has no effect.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

+24 -12
+7 -1
drivers/vhost/net.c
··· 967 967 struct socket *tx_sock = NULL; 968 968 struct socket *rx_sock = NULL; 969 969 long err; 970 + struct vhost_memory *memory; 970 971 971 972 mutex_lock(&n->dev.mutex); 972 973 err = vhost_dev_check_owner(&n->dev); 973 974 if (err) 974 975 goto done; 976 + memory = vhost_dev_reset_owner_prepare(); 977 + if (!memory) { 978 + err = -ENOMEM; 979 + goto done; 980 + } 975 981 vhost_net_stop(n, &tx_sock, &rx_sock); 976 982 vhost_net_flush(n); 977 - err = vhost_dev_reset_owner(&n->dev); 983 + vhost_dev_reset_owner(&n->dev, memory); 978 984 vhost_net_vq_reset(n); 979 985 done: 980 986 mutex_unlock(&n->dev.mutex);
+8 -1
drivers/vhost/test.c
··· 219 219 { 220 220 void *priv = NULL; 221 221 long err; 222 + struct vhost_memory *memory; 223 + 222 224 mutex_lock(&n->dev.mutex); 223 225 err = vhost_dev_check_owner(&n->dev); 224 226 if (err) 225 227 goto done; 228 + memory = vhost_dev_reset_owner_prepare(); 229 + if (!memory) { 230 + err = -ENOMEM; 231 + goto done; 232 + } 226 233 vhost_test_stop(n, &priv); 227 234 vhost_test_flush(n); 228 - err = vhost_dev_reset_owner(&n->dev); 235 + vhost_dev_reset_owner(&n->dev, memory); 229 236 done: 230 237 mutex_unlock(&n->dev.mutex); 231 238 return err;
+7 -9
drivers/vhost/vhost.c
··· 386 386 return err; 387 387 } 388 388 389 - /* Caller should have device mutex */ 390 - long vhost_dev_reset_owner(struct vhost_dev *dev) 389 + struct vhost_memory *vhost_dev_reset_owner_prepare(void) 391 390 { 392 - struct vhost_memory *memory; 391 + return kmalloc(offsetof(struct vhost_memory, regions), GFP_KERNEL); 392 + } 393 393 394 - /* Restore memory to default empty mapping. */ 395 - memory = kmalloc(offsetof(struct vhost_memory, regions), GFP_KERNEL); 396 - if (!memory) 397 - return -ENOMEM; 398 - 394 + /* Caller should have device mutex */ 395 + void vhost_dev_reset_owner(struct vhost_dev *dev, struct vhost_memory *memory) 396 + { 399 397 vhost_dev_cleanup(dev, true); 400 398 399 + /* Restore memory to default empty mapping. */ 401 400 memory->nregions = 0; 402 401 RCU_INIT_POINTER(dev->memory, memory); 403 - return 0; 404 402 } 405 403 406 404 void vhost_dev_stop(struct vhost_dev *dev)
+2 -1
drivers/vhost/vhost.h
··· 136 136 137 137 long vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue **vqs, int nvqs); 138 138 long vhost_dev_check_owner(struct vhost_dev *); 139 - long vhost_dev_reset_owner(struct vhost_dev *); 139 + struct vhost_memory *vhost_dev_reset_owner_prepare(void); 140 + void vhost_dev_reset_owner(struct vhost_dev *, struct vhost_memory *); 140 141 void vhost_dev_cleanup(struct vhost_dev *, bool locked); 141 142 void vhost_dev_stop(struct vhost_dev *); 142 143 long vhost_dev_ioctl(struct vhost_dev *, unsigned int ioctl, void __user *argp);