varnish / varnish/tinykvm

dup2 registers the stale fd instead of the dup() result, leaking a host fd per call

Open Beginner friendly
#99 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
C++
Stars
821
Forks
23
PR merge metrics
No merged PRs in 30d

Description

Summary

The SYS_dup2 handler (lib/tinykvm/linux/system_calls.cpp:1066) registers
the old translation instead of the fd it just created:

const int result = dup(fd);
if (result < 0) {
    regs.sysret() = -errno;
} else {
    cpu.machine().fds().manage_as(new_vfd, new_fd, false, false);
    //                                     ^^^^^^ should be `result`
    regs.sysret() = new_vfd;
}

new_fd is the previous translation of new_vfd (or -1 if there was none).
The fd returned by dup() is never recorded, so it is leaked, and the virtual
fd is bound to an fd that is stale or invalid.

Three observed shapes

  1. Wrong return + fd leak. dup2(1, 5) onto an unused virtual fd returns
    -EBADF where Linux returns success, and leaks one host fd per call. 64
    calls leave +64 host fds open after the VM is destroyed.
  2. Binding to a closed fd. When new_vfd did have a prior translation, the
    handler closes that host fd earlier in the function and then re-registers
    it: fcntl(76, F_GETFD) on the resulting vfd returns -1.
  3. Aliasing after fd recycling. Once the kernel recycles the closed fd
    number, the stale vfd and a legitimately-opened new vfd refer to the same
    host fd. A byte written into a newly created pipe was read back through the
    stale vfd, which per dup2 semantics should be a different pipe's write
    end. In a multi-VM host process the recycling open() can come from another
    tenant; we did not demonstrate that variant, but nothing structurally
    prevents it.

Fix

cpu.machine().fds().manage_as(new_vfd, result, false, false);

and close(result) on the path where manage_as throws, so the failure case
does not leak either.

Test

tests/unit/syscalls.cpp — "dup2 onto a fresh virtual fd must succeed and not
go stale". Fails on the current tree with FAILED: 2 == 0.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the SYS_dup2 handler in lib/tinykvm/linux/system_calls.cpp:1066 and run the named test in tests/unit/syscalls.cpp. Verify that the virtual fd is registered with the dup() result and that a manage_as failure does not leak it. Done means the fresh-virtual-fd test passes and the observed stale-fd and leak behavior is corrected.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
operating-systems
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
86/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.