assorted variants of irqfd setup: convert to CLASS(fd)

in all of those failure exits prior to fdget() are plain returns and
the only thing done after fdput() is (on failure exits) a kfree(),
which can be done before fdput() just fine.

NOTE: in acrn_irqfd_assign() 'fail:' failure exit is wrong for
eventfd_ctx_fileget() failure (we only want fdput() there) and once
we stop doing that, it doesn't need to check if eventfd is NULL or
ERR_PTR(...) there.

NOTE: in privcmd we move fdget() up before the allocation - more
to the point, before the copy_from_user() attempt.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2024-07-20 01:48:34 -04:00
parent 8935989798
commit 66635b0776
4 changed files with 14 additions and 47 deletions

View File

@ -113,7 +113,6 @@ int vfio_virqfd_enable(void *opaque,
void (*thread)(void *, void *), void (*thread)(void *, void *),
void *data, struct virqfd **pvirqfd, int fd) void *data, struct virqfd **pvirqfd, int fd)
{ {
struct fd irqfd;
struct eventfd_ctx *ctx; struct eventfd_ctx *ctx;
struct virqfd *virqfd; struct virqfd *virqfd;
int ret = 0; int ret = 0;
@ -133,8 +132,8 @@ int vfio_virqfd_enable(void *opaque,
INIT_WORK(&virqfd->inject, virqfd_inject); INIT_WORK(&virqfd->inject, virqfd_inject);
INIT_WORK(&virqfd->flush_inject, virqfd_flush_inject); INIT_WORK(&virqfd->flush_inject, virqfd_flush_inject);
irqfd = fdget(fd); CLASS(fd, irqfd)(fd);
if (!fd_file(irqfd)) { if (fd_empty(irqfd)) {
ret = -EBADF; ret = -EBADF;
goto err_fd; goto err_fd;
} }
@ -142,7 +141,7 @@ int vfio_virqfd_enable(void *opaque,
ctx = eventfd_ctx_fileget(fd_file(irqfd)); ctx = eventfd_ctx_fileget(fd_file(irqfd));
if (IS_ERR(ctx)) { if (IS_ERR(ctx)) {
ret = PTR_ERR(ctx); ret = PTR_ERR(ctx);
goto err_ctx; goto err_fd;
} }
virqfd->eventfd = ctx; virqfd->eventfd = ctx;
@ -181,18 +180,9 @@ int vfio_virqfd_enable(void *opaque,
if ((!handler || handler(opaque, data)) && thread) if ((!handler || handler(opaque, data)) && thread)
schedule_work(&virqfd->inject); schedule_work(&virqfd->inject);
} }
/*
* Do not drop the file until the irqfd is fully initialized,
* otherwise we might race against the EPOLLHUP.
*/
fdput(irqfd);
return 0; return 0;
err_busy: err_busy:
eventfd_ctx_put(ctx); eventfd_ctx_put(ctx);
err_ctx:
fdput(irqfd);
err_fd: err_fd:
kfree(virqfd); kfree(virqfd);

View File

@ -112,7 +112,6 @@ static int acrn_irqfd_assign(struct acrn_vm *vm, struct acrn_irqfd *args)
struct eventfd_ctx *eventfd = NULL; struct eventfd_ctx *eventfd = NULL;
struct hsm_irqfd *irqfd, *tmp; struct hsm_irqfd *irqfd, *tmp;
__poll_t events; __poll_t events;
struct fd f;
int ret = 0; int ret = 0;
irqfd = kzalloc(sizeof(*irqfd), GFP_KERNEL); irqfd = kzalloc(sizeof(*irqfd), GFP_KERNEL);
@ -124,8 +123,8 @@ static int acrn_irqfd_assign(struct acrn_vm *vm, struct acrn_irqfd *args)
INIT_LIST_HEAD(&irqfd->list); INIT_LIST_HEAD(&irqfd->list);
INIT_WORK(&irqfd->shutdown, hsm_irqfd_shutdown_work); INIT_WORK(&irqfd->shutdown, hsm_irqfd_shutdown_work);
f = fdget(args->fd); CLASS(fd, f)(args->fd);
if (!fd_file(f)) { if (fd_empty(f)) {
ret = -EBADF; ret = -EBADF;
goto out; goto out;
} }
@ -133,7 +132,7 @@ static int acrn_irqfd_assign(struct acrn_vm *vm, struct acrn_irqfd *args)
eventfd = eventfd_ctx_fileget(fd_file(f)); eventfd = eventfd_ctx_fileget(fd_file(f));
if (IS_ERR(eventfd)) { if (IS_ERR(eventfd)) {
ret = PTR_ERR(eventfd); ret = PTR_ERR(eventfd);
goto fail; goto out;
} }
irqfd->eventfd = eventfd; irqfd->eventfd = eventfd;
@ -162,13 +161,9 @@ static int acrn_irqfd_assign(struct acrn_vm *vm, struct acrn_irqfd *args)
if (events & EPOLLIN) if (events & EPOLLIN)
acrn_irqfd_inject(irqfd); acrn_irqfd_inject(irqfd);
fdput(f);
return 0; return 0;
fail: fail:
if (eventfd && !IS_ERR(eventfd)) eventfd_ctx_put(eventfd);
eventfd_ctx_put(eventfd);
fdput(f);
out: out:
kfree(irqfd); kfree(irqfd);
return ret; return ret;

View File

@ -967,10 +967,11 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
struct privcmd_kernel_irqfd *kirqfd, *tmp; struct privcmd_kernel_irqfd *kirqfd, *tmp;
unsigned long flags; unsigned long flags;
__poll_t events; __poll_t events;
struct fd f;
void *dm_op; void *dm_op;
int ret, idx; int ret, idx;
CLASS(fd, f)(irqfd->fd);
kirqfd = kzalloc(sizeof(*kirqfd) + irqfd->size, GFP_KERNEL); kirqfd = kzalloc(sizeof(*kirqfd) + irqfd->size, GFP_KERNEL);
if (!kirqfd) if (!kirqfd)
return -ENOMEM; return -ENOMEM;
@ -986,8 +987,7 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
kirqfd->dom = irqfd->dom; kirqfd->dom = irqfd->dom;
INIT_WORK(&kirqfd->shutdown, irqfd_shutdown); INIT_WORK(&kirqfd->shutdown, irqfd_shutdown);
f = fdget(irqfd->fd); if (fd_empty(f)) {
if (!fd_file(f)) {
ret = -EBADF; ret = -EBADF;
goto error_kfree; goto error_kfree;
} }
@ -995,7 +995,7 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
kirqfd->eventfd = eventfd_ctx_fileget(fd_file(f)); kirqfd->eventfd = eventfd_ctx_fileget(fd_file(f));
if (IS_ERR(kirqfd->eventfd)) { if (IS_ERR(kirqfd->eventfd)) {
ret = PTR_ERR(kirqfd->eventfd); ret = PTR_ERR(kirqfd->eventfd);
goto error_fd_put; goto error_kfree;
} }
/* /*
@ -1028,20 +1028,11 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
irqfd_inject(kirqfd); irqfd_inject(kirqfd);
srcu_read_unlock(&irqfds_srcu, idx); srcu_read_unlock(&irqfds_srcu, idx);
/*
* Do not drop the file until the kirqfd is fully initialized, otherwise
* we might race against the EPOLLHUP.
*/
fdput(f);
return 0; return 0;
error_eventfd: error_eventfd:
eventfd_ctx_put(kirqfd->eventfd); eventfd_ctx_put(kirqfd->eventfd);
error_fd_put:
fdput(f);
error_kfree: error_kfree:
kfree(kirqfd); kfree(kirqfd);
return ret; return ret;

View File

@ -304,7 +304,6 @@ static int
kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args) kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
{ {
struct kvm_kernel_irqfd *irqfd, *tmp; struct kvm_kernel_irqfd *irqfd, *tmp;
struct fd f;
struct eventfd_ctx *eventfd = NULL, *resamplefd = NULL; struct eventfd_ctx *eventfd = NULL, *resamplefd = NULL;
int ret; int ret;
__poll_t events; __poll_t events;
@ -327,8 +326,8 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
INIT_WORK(&irqfd->shutdown, irqfd_shutdown); INIT_WORK(&irqfd->shutdown, irqfd_shutdown);
seqcount_spinlock_init(&irqfd->irq_entry_sc, &kvm->irqfds.lock); seqcount_spinlock_init(&irqfd->irq_entry_sc, &kvm->irqfds.lock);
f = fdget(args->fd); CLASS(fd, f)(args->fd);
if (!fd_file(f)) { if (fd_empty(f)) {
ret = -EBADF; ret = -EBADF;
goto out; goto out;
} }
@ -336,7 +335,7 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
eventfd = eventfd_ctx_fileget(fd_file(f)); eventfd = eventfd_ctx_fileget(fd_file(f));
if (IS_ERR(eventfd)) { if (IS_ERR(eventfd)) {
ret = PTR_ERR(eventfd); ret = PTR_ERR(eventfd);
goto fail; goto out;
} }
irqfd->eventfd = eventfd; irqfd->eventfd = eventfd;
@ -440,12 +439,6 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
#endif #endif
srcu_read_unlock(&kvm->irq_srcu, idx); srcu_read_unlock(&kvm->irq_srcu, idx);
/*
* do not drop the file until the irqfd is fully initialized, otherwise
* we might race against the EPOLLHUP
*/
fdput(f);
return 0; return 0;
fail: fail:
@ -458,8 +451,6 @@ kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)
if (eventfd && !IS_ERR(eventfd)) if (eventfd && !IS_ERR(eventfd))
eventfd_ctx_put(eventfd); eventfd_ctx_put(eventfd);
fdput(f);
out: out:
kfree(irqfd); kfree(irqfd);
return ret; return ret;