Skip to content

Conversation

@ppentchev
Copy link

@ppentchev ppentchev commented Oct 27, 2024

Hi,

First of all, thanks a lot for writing and maintaining the Rust reference implementation of varlink!

Here's a proposed fix for #73; the Listener::new() method already records the fact that this listener is actually a connection that has already been established via socket activation, and lets the Listener::accept() method honor that flag.

I have written a trivial varlink server - my varlink-hello GitLab project - to demonstrate the need for this change. Without it, with the stock varlink 11.0.1 crate, the varlink-hello program fails, since Listener::accept() attempts to invoke the accept() method of the "Unix listener" which is actually an already-connected socket.

Now, there are three things I don't completely like about this patch:

  • I'm not overly fond of the use of unsafe { ... } in Listener::accept(). A cleaner way to do that would be to add two new values to the Listener enum, e.g. TCPAccepted and UNIXAccepted, and make Listener::new() create a TcpListener or a UnixListener directly, in its own unsafe { ... } blocks. This might cause a bit of code churn; let me know if I should do it.
  • I currently have no way of testing the Windows implementation; I'd be very grateful if somebody could try to build the varlink-hello program under Windows and see if a) it fails with the currently-released varlink crate, and b) this change fixes it.
  • I am not entirely sure how to write tests for this change. An obvious way would be to copy the test_listen() function in varlink/src/tests.rs, create a socketpair, and go for it, but the problem is that to really test it, the LISTEN_FDS, LISTEN_FDNAMES, and LISTEN_PID environment variables need to be set, which might influence other tests run either at the same time or later. I'd be grateful for any ideas on how to approach this; writing a new binary crate that will only be used for testing almost seems like a viable approach, even though Cargo currently does not support test-only binary crates.

Thanks in advance for your time, and keep up the great work!

G'luck,
Peter

Listener::new() already records the fact that this listener is actually
a connection that has already been established via socket activation.
Let Listener::accept() honor that flag.

Fixes varlink#73.
@ppentchev
Copy link
Author

JFTR, I edited the initial comment to add the third thing I'm not completely sure about.

}
}

pub const fn is_already_accepted(&self) -> bool {
Copy link
Collaborator

Choose a reason for hiding this comment

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

While I know the rest of the code is not consistent about this, having at least a minimal documentation string I think is a good idea for public APIs.

Ok(Box::new(s))
&Listener::TCP(Some(ref l), accepted) => {
if accepted {
unsafe { Ok(Box::new(TcpStream::from_raw_fd(l.as_raw_fd()))) }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Without digging into this code a lot more, there's a reason those functions are unsafe and that reason is exactly what looks like a bug here: The previous listener still thinks it owns the file descriptor.

We can avoid this by deconstructing into an OwnedFd like Box::new(TcpStream::from(l.into::<OwnedFd>())) or so.

But I think we need to back up on this, I'll comment on the original ticket.

@haraldh haraldh requested a review from Copilot October 20, 2025 13:01
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes issue #73 by preventing the accept() method from being called on sockets that are already established connections (e.g., via socket activation). When a Listener is created from an already-connected socket, it should not attempt to call accept() but instead should use the existing connection directly.

Key changes:

  • Added is_already_accepted() method to check if a listener represents an already-connected socket
  • Modified accept() methods to handle already-accepted connections by using from_raw_fd() instead of calling accept()
  • Updated main listen loop to exit early for already-accepted listeners

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +205 to +207
if accepted {
unsafe { Ok(Box::new(TcpStream::from_raw_fd(l.as_raw_fd()))) }
} else {
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

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

Using from_raw_fd() transfers ownership of the file descriptor without validation. This could lead to use-after-free or double-close issues if the original listener is used elsewhere. Consider using a safer approach like cloning the file descriptor with dup() first.

Copilot uses AI. Check for mistakes.
Comment on lines +213 to +214
if *accepted {
unsafe { Ok(Box::new(UnixStream::from_raw_fd(l.as_raw_fd()))) }
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

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

Inconsistent dereferencing pattern: TCP case uses accepted directly while UNIX case uses *accepted. Both should use the same pattern for consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +269 to +271
if accepted {
unsafe { Ok(Box::new(TcpStream::from_raw_fd(l.as_raw_fd()))) }
} else {
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

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

This code is duplicated from the Windows version above (lines 205-210). Consider extracting this logic into a helper method to reduce code duplication.

Copilot uses AI. Check for mistakes.
Comment on lines +277 to +279
if *accepted {
unsafe { Ok(Box::new(UnixStream::from_raw_fd(l.as_raw_fd()))) }
} else {
Copy link

Copilot AI Oct 20, 2025

Choose a reason for hiding this comment

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

This code is duplicated from both the Windows version and the TCP case above. The same dereferencing inconsistency and duplication issues apply here.

Copilot uses AI. Check for mistakes.
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.

2 participants