lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20251225110318.46261-1-zhangtianci.1997@bytedance.com>
Date: Thu, 25 Dec 2025 19:03:18 +0800
From: Zhang Tianci <zhangtianci.1997@...edance.com>
To: miklos@...redi.hu
Cc: linux-fsdevel@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	Zhang Tianci <zhangtianci.1997@...edance.com>,
	Geng Xueyu <gengxueyu.520@...edance.com>,
	Wang Jian <wangjian.pg@...edance.com>,
	Xie Yongji <xieyongji@...edance.com>
Subject: [PATCH] fuse: fix the bug of missing EPOLLET event wakeup

Users using Go have reported an issue to us:
When performing read/write operations with goroutines,
since fuse's file->f_ops->poll is not empty,
read/write operations are conducted via epoll.
Additionally, goroutines use the EPOLLET wake-up mode.

Currently, the implementation of fuse_file_poll has
the following problem:
After receiving EAGAIN during read/write operations,
the goroutine calls epoll_wait again, but then results in
permanent blocking. This is because a wake-up event
is required in EPOLLET mode.

The modification idea of this patch is based on the
implementation of epoll_wait:
After epoll_wait calls ->poll() for EPOLLET events,
it does not reinsert them into the ready list.
In this case, ep_poll_callback() needs to be used to
reinsert them into the ready list, so that the behavior
of EPOLLET is consistent with that of non-EPOLLET.

Reported-by: Geng Xueyu <gengxueyu.520@...edance.com>
Reported-by: Wang Jian <wangjian.pg@...edance.com>
Suggested-by: Xie Yongji <xieyongji@...edance.com>
Signed-off-by: Zhang Tianci <zhangtianci.1997@...edance.com>
---

Here is the reproducer for the bug:

1. Change libfuse/example/passthrough.c's xmp_read() to
   always return -EAGAIN and mount it on /mnt_fuse. 
2. Then create test file:
   dd if=/dev/zero of=/mnt_fuse/test_poll bs=1M count=10
3. compile and run the following test case.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>

#define MAX_EVENTS 10
#define BUF_SIZE 4096

int main() {
    int epfd, nfds, fd;
    struct epoll_event ev, events[MAX_EVENTS];
    char buffer[BUF_SIZE];

    fd = open("/mnt_fuse/test_poll", O_RDWR|O_NONBLOCK|O_CREAT, 0644);
    if (fd == -1) {
	perror("open");
	return -1;
    }

    epfd = epoll_create1(0);
    if (epfd == -1) {
        perror("epoll_create1");
	return -1;
    }

    ev.data.fd = fd;
    ev.events = EPOLLIN | EPOLLET;

    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
        perror("epoll_ctl: fd");
	return -1;
    }

    printf("Epoll is monitoring fd=%d.\n", fd);

    while (1) {
        nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
        if (nfds == -1) {
            if (errno == EINTR) {
		continue;
	    }
            perror("epoll_wait");
	    return -1;
        }

        for (int i = 0; i < nfds; ++i) {
            int current_fd = events[i].data.fd;

            if (current_fd == fd) {
                printf("[Notification] Data arrived on fd\n", fd);

                while (1) {
                    ssize_t count = read(current_fd, buffer, BUF_SIZE);

                    if (count == -1) {
                        if (errno == EAGAIN || errno == EWOULDBLOCK) {
                            printf("[Info] Buffer is empty, waiting for next event...\n");
                            break;
                        } else {
                            perror("read");
                            close(current_fd);
			    return -1;
                        }
                    } else if (count == 0) {
                        printf("[Info] EOF detected. Closing.\n");
                        close(current_fd);
                        return 0;
                    }
                    printf(">>> Read %zd bytes\n", count);
                }
            }
        }
    }

    close(epfd);
    return 0;
}

 fs/fuse/file.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 01bc894e9c2ba..025eea58232c2 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -2735,10 +2735,11 @@ __poll_t fuse_file_poll(struct file *file, poll_table *wait)
 	FUSE_ARGS(args);
 	int err;
 
+	poll_wait(file, &ff->poll_wait, wait);
+
 	if (fm->fc->no_poll)
-		return DEFAULT_POLLMASK;
+		goto no_poll;
 
-	poll_wait(file, &ff->poll_wait, wait);
 	inarg.events = mangle_poll(poll_requested_events(wait));
 
 	/*
@@ -2764,9 +2765,13 @@ __poll_t fuse_file_poll(struct file *file, poll_table *wait)
 		return demangle_poll(outarg.revents);
 	if (err == -ENOSYS) {
 		fm->fc->no_poll = 1;
-		return DEFAULT_POLLMASK;
+		goto no_poll;
 	}
 	return EPOLLERR;
+
+no_poll:
+	wake_up_interruptible_sync(&ff->poll_wait);
+	return DEFAULT_POLLMASK;
 }
 EXPORT_SYMBOL_GPL(fuse_file_poll);
 
-- 
2.39.5


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ