rust-lang / rust-lang/socket2

No sound way to zero-copy from socketaddr_in

Open
#323 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Rust
Stars
870
Forks
305
PR merge metrics
No merged PRs in 30d

Description

This is most painful on Windows, where SOCKADDR_STORAGE is 132 bytes long.

Consider the following naive example code, where the SOCKADDR pointer here is a pointer to an ipv4/ipv6 address obtained via syscall:

unsafe fn sock_addr_to_ip(ptr: *const SOCKADDR, length: i32) -> Option<IpAddr> {
    let sa = socket2::SockAddr::new(*(ptr as *const SOCKADDR_STORAGE), length);
    sa.as_socket().map(|s| s.ip())
}

The issue is that SOCKADDR_STORAGE is larger than SOCKADDR, the pointer is only guaranteed to be valid through length bytes, and so casting it to *const SOCKADDR_STORAGE and dereferencing immediately introduces UB.

The correct code looks more like this:

unsafe fn sock_addr_to_ip(ptr: *const SOCKADDR, length: i32) -> Option<IpAddr> {
    assert!(length as usize <= std::mem::size_of::<SOCKADDR_STORAGE>());
    let mut storage = std::mem::MaybeUninit::<SOCKADDR_STORAGE>::uninit();
    std::ptr::copy_nonoverlapping(ptr, &mut storage as *mut _ as *mut _, length as _);
    let sa = socket2::SockAddr::new(storage.assume_init(), length);
    sa.as_socket().map(|s| s.ip())
}

Since the backing storage for SockAddr is owning, we have to make a copy. It's incredibly awkward and a bit slower. And that's just so we can safely invoke the nice parsing logic in as_socket().

To fix this we'd probably have to add a SockAddrRef<'a> type that doesn't own its backing storage. If that's not immediately feasible, providing a constructor that performs this unsafe copy inside socket2 would also solve the immediate issue.

Contributor guide

Open the contributing guide

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 by reviewing socket2::SockAddr and its as_socket() parsing path, using the issue's SOCKADDR and SOCKADDR_STORAGE examples as the safety boundary. Compare the proposed borrowed SockAddrRef approach with a constructor that performs the copy, and define completion as safe handling of pointers valid only for the supplied length without requiring callers to copy into SOCKADDR_STORAGE.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
networking
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.