Skip to content

Fix EPOLLONESHOT leaks in epoll bridge#5

Merged
jserv merged 1 commit intomainfrom
epoll
May 4, 2026
Merged

Fix EPOLLONESHOT leaks in epoll bridge#5
jserv merged 1 commit intomainfrom
epoll

Conversation

@jserv
Copy link
Copy Markdown
Contributor

@jserv jserv commented May 4, 2026

Two bugs surfaced while auditing EPOLLET fidelity on the kqueue backend, both reproducible against Linux/qemu ground truth:

  1. sys_epoll_pwait result loop did not check oneshot_armed. With multi-filter registrations (e.g. EPOLLIN | EPOLLOUT | EPOLLONESHOT), EV_ONESHOT only removed the filter that fired; a surviving filter could fire later and was reported, violating Linux semantics where the fd stays disarmed until EPOLL_CTL_MOD re-arms it.

  2. sys_epoll_ctl MOD pre-delete batched two EV_DELETE changes in one kevent call with NULL eventlist. When the first delete fails ENOENT (the fired filter was already removed by EV_ONESHOT), kqueue stops processing and the second delete leaks the survivor. Issue each delete in its own kevent call so they are independent.

Also tighten the EPOLLET registration comment to describe what divergence actually requires (a unified drain signal across every data-consuming path, which the bridge does not maintain), and lock the contract in with tests/test-epoll-edge.c . Each new ONESHOT test was confirmed to fail on pre-fix elfuse and pass on post-fix; both pass against Linux/qemu.


Summary by cubic

Fixes EPOLLONESHOT leaks and aligns EPOLLET/ONESHOT behavior in the kqueue epoll bridge with Linux. Prevents spurious events and stale filters; adds targeted tests to lock in behavior.

  • Bug Fixes
    • epoll_wait path now guards on ONESHOT state so multi-filter registrations stay fully disarmed until EPOLL_CTL_MOD re-arms.
    • EPOLL_CTL_MOD issues EV_DELETE per filter instead of batching, avoiding ENOENT aborts that leak surviving filters.
    • Tightened EPOLLET doc comment to note the known partial-read divergence; added test-epoll-edge to validate EPOLLET and EPOLLONESHOT semantics (manifest and test matrix updated).

Written for commit b8d16b8. Summary will update on new commits.

Two bugs surfaced while auditing EPOLLET fidelity on the kqueue backend,
both reproducible against Linux/qemu ground truth:
1. sys_epoll_pwait result loop did not check oneshot_armed. With
   multi-filter registrations (e.g. EPOLLIN | EPOLLOUT | EPOLLONESHOT),
   EV_ONESHOT only removed the filter that fired; a surviving filter
   could fire later and was reported, violating Linux semantics where
   the fd stays disarmed until EPOLL_CTL_MOD re-arms it.

2. sys_epoll_ctl MOD pre-delete batched two EV_DELETE changes in one
   kevent call with NULL eventlist. When the first delete fails ENOENT
   (the fired filter was already removed by EV_ONESHOT), kqueue stops
   processing and the second delete leaks the survivor. Issue each
   delete in its own kevent call so they are independent.

Also tighten the EPOLLET registration comment to describe what divergence
actually requires (a unified drain signal across every data-consuming
path, which the bridge does not maintain), and lock the contract in with
tests/test-epoll-edge.c . Each new ONESHOT test was confirmed to fail on
pre-fix elfuse and pass on post-fix; both pass against Linux/qemu.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

@jserv jserv merged commit efb307a into main May 4, 2026
5 checks passed
@jserv jserv deleted the epoll branch May 4, 2026 23:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant