Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

socket_peercred / SCM_CREDENTIALS: UB when kernel returns pid 0

未关闭
#1,698 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 4 天内回复

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
42/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
活跃
技术栈
linux, rust

调研方向

Start with UCred and Pid in src/net/types.rs, then trace socket_peercred in src/backend/linux_raw/net/sockopt.rs and src/backend/libc/net/sockopt.rs, plus RecvAncillaryMessage::ScmCredentials where the issue identifies an unaligned read. The issue proposes representing an absent pid explicitly and converting from a raw integer-field struct; inspect related issues #1696 and #1678 before choosing the change. Done means both credential paths handle kernel pid 0 without constructing an invalid Pid.

由索引模型根据 Issue 内容生成。

描述

The Linux kernel returns a pid of 0 in a struct ucred in several situations. Rustix turns that struct directly into UCred without checking it. The result is a Pid (NonZeroI32) holding 0, which is undefined behavior.

Where

  • UCred is #[repr(C)] { pid: Pid, uid: Uid, gid: Gid } and Pid wraps NonZeroI32 (src/net/types.rs)
  • socket_peercred is getsockopt::<UCred>(fd, SOL_SOCKET, SO_PEERCRED) (src/backend/linux_raw/net/sockopt.rs, src/backend/libc/net/sockopt.rs): getsockopt fills a MaybeUninit<T> and calls assume_init() but with UCred a pid of 0 is an invalid bit pattern
  • RecvAncillaryMessage::ScmCredentials uses payload.as_ptr().cast::<UCred>().read_unaligned()

When is it 0

socket_peercred / SCM_CREDENTIALS use pid_vnr(), which translates a struct pid into the reader's pid namespace. It returns 0 when the pid is NULL or not visible in that namespace. (reference: https://github.com/torvalds/linux/blob/67f0943b394d920b6c142aad8c6af94340342ae7/kernel/pid.c#L542-L560)

So it's 0 for:

  • socket with no peer credentials e.g. TCP socket or unconnected UNIX socket (SO_PEERCRED succeeds with pid 0 and uid/gid −1)
  • peer that's not visible in the reader's pid namespace e.g. process in a container reading credentials of a peer on the host or in a sibling container. https://man7.org/linux/man-pages/man7/pid_namespaces.7.html mentions that pids passed over Unix sockets are translated into the receiving process's pid namespace.
  • SCM_CREDENTIALS sent without credentials e.g. a message sent before the receiver enabled 'SO_PASSCRED' on a stream socket

https://man7.org/linux/man-pages/man7/unix.7.html and https://man7.org/linux/man-pages/man7/socket.7.html describe SO_PEERCRED and SCM_CREDENTIALS but don't mention the 0 case but see the kernel code references above.

Related

#1696 (getpgid) and #1678 (tcgetpgrp on macOS) are also cases of a pid of 0 from the kernel turned into a Pid.

Reproduction

Cargo.toml:

[package]
name = "repro"
version = "0.1.0"
edition = "2024"

[dependencies]
libc = "0.2"
# "process" only because "net" alone does not build (#1689).
rustix = { version = "=1.1.5", features = ["net", "process"] }

[features]
use-libc = ["rustix/use-libc"]
use std::io::{IoSliceMut, Write};
use std::mem::MaybeUninit;
use std::os::fd::AsRawFd;
use std::os::unix::net::{UnixDatagram, UnixStream};

use rustix::net::{RecvAncillaryBuffer, RecvAncillaryMessage, RecvFlags, recvmsg, sockopt};

fn main() {
   so_peercred_pid_0();
   scm_credentials_pid_0();
}

/// `socket_peercred`: An unbound socket has no peer, so SO_PEERCRED succeeds with pid 0 but rustix returns an error.
fn so_peercred_pid_0() {
   // An unbound socket has no peer, so SO_PEERCRED succeeds with pid 0.
   let socket = UnixDatagram::unbound().unwrap();

   let mut cred = libc::ucred { pid: -1, uid: 0, gid: 0 };
   let mut len = size_of::<libc::ucred>() as libc::socklen_t;
   let ret = unsafe {
      libc::getsockopt(
         socket.as_raw_fd(),
         libc::SOL_SOCKET,
         libc::SO_PEERCRED,
         (&raw mut cred).cast(),
         &mut len,
      )
   };
   println!("SO_PEERCRED libc:   ret={ret} pid={} uid={} gid={}", cred.pid, cred.uid as i32, cred.gid as i32);

   println!("SO_PEERCRED rustix: {:?}", sockopt::socket_peercred(&socket));
}

/// `RecvAncillaryMessage::ScmCredentials`: message sent before the receiver set SO_PASSCRED has no credentials so kernel succeeds with pid 0
fn scm_credentials_pid_0() {
   let (mut sender, receiver) = UnixStream::pair().unwrap();
   sender.write_all(b"x").unwrap();
   sockopt::set_socket_passcred(&receiver, true).unwrap();

   let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmCredentials(1))];
   let mut control = RecvAncillaryBuffer::new(&mut space);
   recvmsg(&receiver, &mut [IoSliceMut::new(&mut [0])], &mut control, RecvFlags::empty()).unwrap();
   for message in control.drain() {
      if let RecvAncillaryMessage::ScmCredentials(cred) = message {
         // A `Pid` can't be 0 so the compiler may turn this into `false`.
         println!(
            "SCM_CREDENTIALS rustix: {cred:?}, pid == 0: {}",
            cred.pid.as_raw_pid() == 0
         );
      }
   }
}

uid is returned as Errno

raw: cargo run - uid bits truncated to 16 bits and negated: -1 -> 1 (EPERM)

SO_PEERCRED libc:   ret=0 pid=0 uid=-1 gid=-1
SO_PEERCRED rustix: Err(Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" })
SCM_CREDENTIALS rustix: UCred { pid: Pid(0), uid: Uid(65534), gid: Gid(65534) }, pid == 0: true

libc: cargo run --features use-libc - uid -1 -> -1

SO_PEERCRED libc:   ret=0 pid=0 uid=-1 gid=-1
SO_PEERCRED rustix: Err(Os { code: -1, kind: Uncategorized, message: "Unknown error -1" })
SCM_CREDENTIALS rustix: UCred { pid: Pid(0), uid: Uid(65534), gid: Gid(65534) }, pid == 0: true

Idea

(Breaking) make UCred::pid an Option<Pid>: make explicit that a pid may be absent. std's UCred does the same.

  • read the kernel's bytes into a private raw struct e.g. RawUCred with plain integer fields and convert explicitly
  • msg.rs can do the same with read_unaligned::<RawUCred>()
主要语言
Rust
星标
2.1k
派生
301
平均合并
10 天 1 小时
30 天内合并 PR
1

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

bytecodealliance/rustix 的其他 Issue

查看 bytecodealliance/rustix 的全部 Issue

相似的 Issue

更多 Rust Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。