rustix 0.38.8 社区漏洞 CVE-2024-43806 修复

修复措施:升级rustix版本到0.38.16版本

Signed-off-by: ljy9810 <longjianyin@h-partners.com>
This commit is contained in:
ljy9810
2025-07-01 19:30:21 +08:00
parent 294a2d73d6
commit 5d9c62cc10
26 changed files with 768 additions and 457 deletions
+19 -13
View File
@@ -1,16 +1,22 @@
# Implementation derived from `.cirrus.yml` in Rust's libc bindings
# at revision 7f4774e76bd5cb9ccb7140d71ef9be9c16009cdf.
task:
name: stable x86_64-unknown-freebsd-13
freebsd_instance:
image_family: freebsd-13-0-snap
setup_script:
- pkg install -y curl
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh --default-toolchain stable -y --profile=minimal
- . $HOME/.cargo/env
- rustup default stable
test_script:
- . $HOME/.cargo/env
- cargo test --workspace --features=all-apis
# Disable FreeBSD testing for now, as we currently hit this error:
#
# [4/4] Extracting curl-7.88.1: .......... done
# curl https://sh.rustup.rs -sSf --output rustup.sh
# ld-elf.so.1: /usr/local/lib/libcurl.so.4: Undefined symbol "nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation"
#task:
# name: stable x86_64-unknown-freebsd-13
# freebsd_instance:
# image_family: freebsd-13-1
# setup_script:
# - pkg install -y curl
# - curl https://sh.rustup.rs -sSf --output rustup.sh
# - sh rustup.sh --default-toolchain stable -y --profile=minimal
# - . $HOME/.cargo/env
# - rustup default stable
# test_script:
# - . $HOME/.cargo/env
# - cargo test --workspace --features=all-apis
+41 -163
View File
@@ -62,12 +62,9 @@ jobs:
i686-unknown-linux-gnu
i686-unknown-linux-musl
wasm32-unknown-emscripten
riscv64gc-unknown-linux-gnu
aarch64-unknown-linux-gnu
aarch64-unknown-linux-musl
powerpc64le-unknown-linux-gnu
mipsel-unknown-linux-gnu
mips64el-unknown-linux-gnuabi64
armv5te-unknown-linux-gnueabi
s390x-unknown-linux-gnu
arm-linux-androideabi
@@ -75,20 +72,31 @@ jobs:
sparcv9-sun-solaris
aarch64-linux-android
aarch64-apple-ios
- if: matrix.rust == 'nightly'
wasm32-wasi
- if: matrix.rust != '1.48'
run: rustup target add x86_64-unknown-fuchsia
- if: matrix.rust != 'nightly'
- if: matrix.rust == '1.48'
run: rustup target add x86_64-fuchsia
- name: Install cross-compilation tools
run: |
set -ex
sudo apt-get update
sudo apt-get install -y gcc-i686-linux-gnu gcc-aarch64-linux-gnu gcc-riscv64-linux-gnu gcc-arm-linux-gnueabi musl-tools
sudo apt-get install -y gcc-i686-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi musl-tools
- name: Use specific dependency versions for Rust 1.48 compatibility.
if: matrix.rust == '1.48'
run: cargo update --package=once_cell --precise 1.14.0
run: |
cargo update --package=once_cell --precise 1.14.0
cargo update --package=tempfile --precise=3.4.0
cargo update --package=io-lifetimes --precise 1.0.6
cargo update --package=flate2 --precise=1.0.25
for crate in test-crates/*; do
cd $crate
cargo update --package=io-lifetimes --precise 1.0.6
cd - >/dev/null
done
cargo update --package=tempfile --precise=3.6.0
- run: cargo check --workspace --release -vv --all-targets
- run: cargo check --workspace --release -vv --features=all-apis --all-targets
@@ -102,22 +110,19 @@ jobs:
- run: cargo check --workspace --release -vv --target=x86_64-apple-darwin --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=x86_64-unknown-freebsd --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=x86_64-unknown-netbsd --features=all-apis --all-targets
- if: matrix.rust == 'nightly'
- if: matrix.rust != '1.48'
run: cargo check --workspace --release -vv --target=x86_64-unknown-fuchsia --features=all-apis --all-targets
- if: matrix.rust != 'nightly'
- if: matrix.rust == '1.48'
run: cargo check --workspace --release -vv --target=x86_64-fuchsia --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=x86_64-unknown-illumos --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=i686-unknown-linux-gnu --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=i686-unknown-linux-musl --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=i686-unknown-linux-musl --features=use-libc,all-apis --all-targets
- run: cargo check --workspace --release -vv --target=wasm32-unknown-emscripten --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=riscv64gc-unknown-linux-gnu --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=aarch64-unknown-linux-gnu --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=aarch64-unknown-linux-musl --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=aarch64-unknown-linux-musl --features=use-libc,all-apis --all-targets
- run: cargo check --workspace --release -vv --target=powerpc64le-unknown-linux-gnu --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=mipsel-unknown-linux-gnu --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=mips64el-unknown-linux-gnuabi64 --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=armv5te-unknown-linux-gnueabi --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=s390x-unknown-linux-gnu --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=arm-linux-androideabi --features=all-apis --all-targets
@@ -125,6 +130,10 @@ jobs:
- run: cargo check --workspace --release -vv --target=sparcv9-sun-solaris --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=aarch64-apple-ios --features=all-apis --all-targets
- run: cargo check --workspace --release -vv --target=aarch64-linux-android --features=all-apis --all-targets
# Omit --all-targets for WASI until all the dev-dependencies support WASI
# on stable.
- if: matrix.rust != '1.48'
run: cargo check --workspace --release -vv --target=wasm32-wasi --features=all-apis
check_no_default_features:
name: Check --no-default-features
@@ -204,6 +213,8 @@ jobs:
- run: cargo check -Z build-std --target x86_64-unknown-dragonfly --all-targets --features=all-apis
- run: cargo check -Z build-std --target sparc-unknown-linux-gnu --all-targets --features=all-apis
- run: cargo check -Z build-std --target armv7-unknown-freebsd --all-targets --features=all-apis
# Omit --all-targets on gnu_ilp32 because dev-dependency tempfile depends on an older rustix
- run: cargo check -Z build-std --target aarch64-unknown-linux-gnu_ilp32 --features=all-apis
# Omit --all-targets on haiku because not all the tests build yet.
- run: cargo check -Z build-std --target x86_64-unknown-haiku --features=all-apis
# x86_64-uwp-windows-msvc isn't currently working.
@@ -216,13 +227,13 @@ jobs:
QEMU_BUILD_VERSION: 7.0.0
strategy:
matrix:
build: [ubuntu, ubuntu-18.04, i686-linux, aarch64-linux, powerpc64le-linux, riscv64-linux, s390x-linux, arm-linux, ubuntu-stable, ubuntu-1.48, i686-linux-stable, aarch64-linux-stable, riscv64-linux-stable, s390x-linux-stable, mipsel-linux-stable, mips64el-linux-stable, powerpc64le-linux-stable, arm-linux-stable, ubuntu-1.48, i686-linux-1.48, aarch64-linux-1.48, riscv64-linux-1.48, s390x-linux-1.48, mipsel-linux-1.48, mips64el-linux-1.48, powerpc64le-linux-1.48, arm-linux-1.48, macos-latest, macos-10.15, windows, windows-2019]
build: [ubuntu, ubuntu-20.04, i686-linux, aarch64-linux, powerpc64le-linux, s390x-linux, arm-linux, ubuntu-stable, ubuntu-1.48, i686-linux-stable, aarch64-linux-stable, s390x-linux-stable, powerpc64le-linux-stable, arm-linux-stable, ubuntu-1.48, i686-linux-1.48, aarch64-linux-1.48, s390x-linux-1.48, powerpc64le-linux-1.48, arm-linux-1.48, macos-latest, macos-11, windows, windows-2019]
include:
- build: ubuntu
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: nightly
- build: ubuntu-18.04
os: ubuntu-18.04
- build: ubuntu-20.04
os: ubuntu-20.04
rust: nightly
- build: i686-linux
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
@@ -249,33 +260,6 @@ jobs:
qemu: qemu-ppc64le
qemu_args: -L /usr/powerpc64le-linux-gnu
qemu_target: ppc64le-linux-user
- build: mips64el-linux
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: nightly
target: mips64el-unknown-linux-gnuabi64
gcc_package: gcc-mips64el-linux-gnuabi64
gcc: mips64el-linux-gnuabi64-gcc
qemu: qemu-mips64el
qemu_args: -L /usr/mips64el-linux-gnuabi64
qemu_target: mips64el-linux-user
- build: mipsel-linux
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: nightly
target: mipsel-unknown-linux-gnu
gcc_package: gcc-mipsel-linux-gnu
gcc: mipsel-linux-gnu-gcc
qemu: qemu-mipsel
qemu_args: -L /usr/mipsel-linux-gnu
qemu_target: mipsel-linux-user
- build: riscv64-linux
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: nightly
target: riscv64gc-unknown-linux-gnu
gcc_package: gcc-riscv64-linux-gnu
gcc: riscv64-linux-gnu-gcc
qemu: qemu-riscv64
qemu_args: -L /usr/riscv64-linux-gnu
qemu_target: riscv64-linux-user
- build: s390x-linux
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: nightly
@@ -313,15 +297,6 @@ jobs:
qemu: qemu-aarch64
qemu_args: -L /usr/aarch64-linux-gnu
qemu_target: aarch64-linux-user
- build: riscv64-linux-stable
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: stable
target: riscv64gc-unknown-linux-gnu
gcc_package: gcc-riscv64-linux-gnu
gcc: riscv64-linux-gnu-gcc
qemu: qemu-riscv64
qemu_args: -L /usr/riscv64-linux-gnu
qemu_target: riscv64-linux-user
- build: s390x-linux-stable
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: stable
@@ -340,24 +315,6 @@ jobs:
qemu: qemu-ppc64le
qemu_args: -L /usr/powerpc64le-linux-gnu
qemu_target: ppc64le-linux-user
- build: mips64el-linux-stable
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: stable
target: mips64el-unknown-linux-gnuabi64
gcc_package: gcc-mips64el-linux-gnuabi64
gcc: mips64el-linux-gnuabi64-gcc
qemu: qemu-mips64el
qemu_args: -L /usr/mips64el-linux-gnuabi64
qemu_target: mips64el-linux-user
- build: mipsel-linux-stable
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: stable
target: mipsel-unknown-linux-gnu
gcc_package: gcc-mipsel-linux-gnu
gcc: mipsel-linux-gnu-gcc
qemu: qemu-mipsel
qemu_args: -L /usr/mipsel-linux-gnu
qemu_target: mipsel-linux-user
- build: arm-linux-stable
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: stable
@@ -386,15 +343,6 @@ jobs:
qemu: qemu-aarch64
qemu_args: -L /usr/aarch64-linux-gnu
qemu_target: aarch64-linux-user
- build: riscv64-linux-1.48
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: 1.48
target: riscv64gc-unknown-linux-gnu
gcc_package: gcc-riscv64-linux-gnu
gcc: riscv64-linux-gnu-gcc
qemu: qemu-riscv64
qemu_args: -L /usr/riscv64-linux-gnu
qemu_target: riscv64-linux-user
- build: s390x-linux-1.48
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: 1.48
@@ -413,24 +361,6 @@ jobs:
qemu: qemu-ppc64le
qemu_args: -L /usr/powerpc64le-linux-gnu
qemu_target: ppc64le-linux-user
- build: mips64el-linux-1.48
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: 1.48
target: mips64el-unknown-linux-gnuabi64
gcc_package: gcc-mips64el-linux-gnuabi64
gcc: mips64el-linux-gnuabi64-gcc
qemu: qemu-mips64el
qemu_args: -L /usr/mips64el-linux-gnuabi64
qemu_target: mips64el-linux-user
- build: mipsel-linux-1.48
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: 1.48
target: mipsel-unknown-linux-gnu
gcc_package: gcc-mipsel-linux-gnu
gcc: mipsel-linux-gnu-gcc
qemu: qemu-mipsel
qemu_args: -L /usr/mipsel-linux-gnu
qemu_target: mipsel-linux-user
- build: arm-linux-1.48
os: ubuntu-20.04 # TODO: remove pin when fixed (#483)
rust: 1.48
@@ -443,8 +373,8 @@ jobs:
- build: macos-latest
os: macos-latest
rust: stable
- build: macos-10.15
os: macos-10.15
- build: macos-11
os: macos-11
rust: stable
- build: windows
os: windows-latest
@@ -495,11 +425,6 @@ jobs:
upcase=$(echo ${{ matrix.target }} | awk '{ print toupper($0) }' | sed 's/-/_/g')
echo CARGO_TARGET_${upcase}_RUNNER=${{ runner.tool_cache }}/qemu/bin/${{ matrix.qemu }} ${{ matrix.qemu_args }} >> $GITHUB_ENV
# See if qemu is already in the cache
if [ -f ${{ runner.tool_cache }}/qemu/bin/${{ matrix.qemu }} ]; then
exit 0
fi
# Download and build qemu from source since the most recent release is
# way faster at arm emulation than the current version github actions'
# ubuntu image uses. Disable as much as we can to get it to build
@@ -516,7 +441,17 @@ jobs:
- name: Use specific dependency versions for Rust 1.48 compatibility.
if: matrix.rust == '1.48'
run: cargo update --package=once_cell --precise 1.14.0
run: |
cargo update --package=once_cell --precise 1.14.0
cargo update --package=tempfile --precise=3.4.0
cargo update --package=io-lifetimes --precise 1.0.6
cargo update --package=flate2 --precise=1.0.25
for crate in test-crates/*; do
cd $crate
cargo update --package=io-lifetimes --precise 1.0.6
cd - >/dev/null
done
cargo update --package=tempfile --precise=3.6.0
- run: |
# Run the tests, and check the prebuilt release libraries.
@@ -555,7 +490,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
build: [ubuntu, i686-linux, aarch64-linux, powerpc64le-linux, mips64el-linux, mipsel-linux, riscv64-linux, arm-linux]
build: [ubuntu, i686-linux, aarch64-linux, powerpc64le-linux, arm-linux]
include:
- build: ubuntu
os: ubuntu-latest
@@ -585,33 +520,6 @@ jobs:
qemu: qemu-ppc64le
qemu_args: -L /usr/powerpc64le-linux-gnu
qemu_target: ppc64le-linux-user
- build: mips64el-linux
os: ubuntu-latest
rust: stable
target: mips64el-unknown-linux-gnuabi64
gcc_package: gcc-mips64el-linux-gnuabi64
gcc: mips64el-linux-gnuabi64-gcc
qemu: qemu-mips64el
qemu_args: -L /usr/mips64el-linux-gnuabi64
qemu_target: mips64el-linux-user
- build: mipsel-linux
os: ubuntu-latest
rust: stable
target: mipsel-unknown-linux-gnu
gcc_package: gcc-mipsel-linux-gnu
gcc: mipsel-linux-gnu-gcc
qemu: qemu-mipsel
qemu_args: -L /usr/mipsel-linux-gnu
qemu_target: mipsel-linux-user
- build: riscv64-linux
os: ubuntu-latest
rust: stable
target: riscv64gc-unknown-linux-gnu
gcc_package: gcc-riscv64-linux-gnu
gcc: riscv64-linux-gnu-gcc
qemu: qemu-riscv64
qemu_args: -L /usr/riscv64-linux-gnu
qemu_target: riscv64-linux-user
- build: arm-linux
os: ubuntu-latest
rust: stable
@@ -668,11 +576,6 @@ jobs:
upcase=$(echo ${{ matrix.target }} | awk '{ print toupper($0) }' | sed 's/-/_/g')
echo CARGO_TARGET_${upcase}_RUNNER=${{ runner.tool_cache }}/qemu/bin/${{ matrix.qemu }} ${{ matrix.qemu_args }} >> $GITHUB_ENV
# See if qemu is already in the cache
if [ -f ${{ runner.tool_cache }}/qemu/bin/${{ matrix.qemu }} ]; then
exit 0
fi
# Download and build qemu from source since the most recent release is
# way faster at arm emulation than the current version github actions'
# ubuntu image uses. Disable as much as we can to get it to build
@@ -697,7 +600,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
build: [powerpc64le-linux, mipsel-linux, mips64el-linux]
build: [powerpc64le-linux]
include:
- build: powerpc64le-linux
os: ubuntu-latest
@@ -708,31 +611,11 @@ jobs:
qemu: qemu-ppc64le
qemu_args: -L /usr/powerpc64le-linux-gnu
qemu_target: ppc64le-linux-user
- build: mips64el-linux
os: ubuntu-latest
rust: nightly
target: mips64el-unknown-linux-gnuabi64
gcc_package: gcc-mips64el-linux-gnuabi64
gcc: mips64el-linux-gnuabi64-gcc
qemu: qemu-mips64el
qemu_args: -L /usr/mips64el-linux-gnuabi64
qemu_target: mips64el-linux-user
- build: mipsel-linux
os: ubuntu-latest
rust: nightly
target: mipsel-unknown-linux-gnu
gcc_package: gcc-mipsel-linux-gnu
gcc: mipsel-linux-gnu-gcc
qemu: qemu-mipsel
qemu_args: -L /usr/mipsel-linux-gnu
qemu_target: mipsel-linux-user
env:
# -D warnings is commented out in our install-rust action; re-add it here.
RUSTFLAGS: --cfg rustix_use_experimental_asm -D warnings
RUSTDOCFLAGS: --cfg rustix_use_experimental_asm
CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_RUSTFLAGS: --cfg rustix_use_experimental_asm
CARGO_TARGET_MIPSEL_UNKNOWN_LINUX_GNU_RUSTFLAGS: --cfg rustix_use_experimental_asm
CARGO_TARGET_MIPS64EL_UNKNOWN_LINUX_GNUABI64_RUSTFLAGS: --cfg rustix_use_experimental_asm
QEMU_BUILD_VERSION: 7.0.0
steps:
- uses: actions/checkout@v3
@@ -777,11 +660,6 @@ jobs:
upcase=$(echo ${{ matrix.target }} | awk '{ print toupper($0) }' | sed 's/-/_/g')
echo CARGO_TARGET_${upcase}_RUNNER=${{ runner.tool_cache }}/qemu/bin/${{ matrix.qemu }} ${{ matrix.qemu_args }} >> $GITHUB_ENV
# See if qemu is already in the cache
if [ -f ${{ runner.tool_cache }}/qemu/bin/${{ matrix.qemu }} ]; then
exit 0
fi
# Download and build qemu from source since the most recent release is
# way faster at arm emulation than the current version github actions'
# ubuntu image uses. Disable as much as we can to get it to build
+1 -1
View File
@@ -21,7 +21,7 @@ if (host_os != "linux" || host_cpu != "arm64") {
sources = [ "src/lib.rs" ]
edition = "2018"
cargo_pkg_version = "0.36.8"
cargo_pkg_version = "0.36.16"
cargo_pkg_authors = "Dan Gohman <dev@sunfishcode.online>, Jakub Konka <kubkon@jakubkonka.com>"
cargo_pkg_name = "rustix"
cargo_pkg_description =
+4 -16
View File
@@ -1,6 +1,6 @@
[package]
name = "rustix"
version = "0.36.8"
version = "0.36.16"
authors = [
"Dan Gohman <dev@sunfishcode.online>",
"Jakub Konka <kubkon@jakubkonka.com>",
@@ -40,7 +40,7 @@ once_cell = { version = "1.5.2", optional = true }
# `RUSTFLAGS` or enabling the `use-libc` cargo feature.
[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"), all(target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "powerpc64", target_arch = "riscv64", target_arch = "mips", target_arch = "mips64")))))'.dependencies]
linux-raw-sys = { version = "0.1.2", default-features = false, features = ["general", "errno", "ioctl", "no_std"] }
libc_errno = { package = "errno", version = "0.2.8", default-features = false, optional = true }
libc_errno = { package = "errno", version = "0.3.0", default-features = false, optional = true }
libc = { version = "0.2.133", features = ["extra_traits"], optional = true }
# Dependencies for platforms where only libc is supported:
@@ -48,7 +48,7 @@ libc = { version = "0.2.133", features = ["extra_traits"], optional = true }
# On all other Unix-family platforms, and under Miri, we always use the libc
# backend, so enable its dependencies unconditionally.
[target.'cfg(any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"), all(target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "powerpc64", target_arch = "riscv64", target_arch = "mips", target_arch = "mips64")))))))'.dependencies]
libc_errno = { package = "errno", version = "0.2.8", default-features = false }
libc_errno = { package = "errno", version = "0.3.0", default-features = false }
libc = { version = "0.2.133", features = ["extra_traits"] }
# Additional dependencies for Linux with the libc backend:
@@ -71,26 +71,14 @@ features = [
[dev-dependencies]
tempfile = "3.2.0"
libc = "0.2.133"
libc_errno = { package = "errno", version = "0.2.8", default-features = false }
libc_errno = { package = "errno", version = "0.3.0", default-features = false }
io-lifetimes = { version = "1.0.0", default-features = false, features = ["close"] }
# Don't upgrade to serial_test 0.7 for now because it depends on a
# `parking_lot_core` version which is not compatible with our MSRV of 1.48.
serial_test = "0.6"
memoffset = "0.7.1"
flate2 = "1.0"
[target.'cfg(all(criterion, not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies]
criterion = "0.4"
[target.'cfg(windows)'.dev-dependencies]
ctor = "0.1.21"
# Add Criterion configuration, as described here:
# <https://bheisler.github.io/criterion.rs/book/getting_started.html#step-1---add-dependency-to-cargotoml>
[[bench]]
name = "mod"
harness = false
[package.metadata.docs.rs]
features = ["all-apis"]
rustdoc-args = ["--cfg", "doc_cfg"]
+1 -1
View File
@@ -3,7 +3,7 @@
"Name": "rustix",
"License": "Apache-2.0_WITH_LLVM-exception, Apache License 2.0, MIT",
"License File": "LICENSE-Apache-2.0_WITH_LLVM-exception, LICENSE-APACHE, LICENSE-MIT",
"Version Number": "0.36.8",
"Version Number": "0.36.16",
"Owner": "fangting12@huawei.com",
"Upstream URL": "https://github.com/bytecodealliance/rustix",
"Description": "A Rust library that provides support for working with UTF-8 encoded strings."
+3 -2
View File
@@ -87,8 +87,8 @@ by default. The rest of the API is conditional with cargo feature flags:
## Similar crates
`rustix` is similar to [`nix`], [`simple_libc`], [`unix`], [`nc`], and
[`uapi`]. `rustix` is architected for [I/O safety] with most APIs using
`rustix` is similar to [`nix`], [`simple_libc`], [`unix`], [`nc`], [`uapi`],
and [`rusl`]. `rustix` is architected for [I/O safety] with most APIs using
[`OwnedFd`] and [`AsFd`] to manipulate file descriptors rather than `File` or
even `c_int`, and supporting multiple backends so that it can use direct
syscalls while still being usable on all platforms `libc` supports. Like `nix`,
@@ -136,6 +136,7 @@ version of this crate.
[`nc`]: https://crates.io/crates/nc
[`simple_libc`]: https://crates.io/crates/simple_libc
[`uapi`]: https://crates.io/crates/uapi
[`rusl`]: https://lib.rs/crates/rusl
[`relibc`]: https://github.com/redox-os/relibc
[`syscall`]: https://crates.io/crates/syscall
[`sc`]: https://crates.io/crates/sc
-182
View File
@@ -1,182 +0,0 @@
/// Benchmarks for rustix.
///
/// To enable these benchmarks, add `--cfg=criterion` to RUSTFLAGS and enable
/// the "fs", "time", and "process" cargo features.
#[cfg(any(
not(criterion),
not(feature = "fs"),
not(feature = "process"),
not(feature = "time"),
windows,
target_os = "emscripten",
target_os = "redox",
target_os = "wasi",
))]
fn main() {
unimplemented!()
}
#[cfg(not(any(
not(criterion),
not(feature = "fs"),
not(feature = "process"),
not(feature = "time"),
windows,
target_os = "emscripten",
target_os = "redox",
target_os = "wasi",
)))]
use criterion::{criterion_group, criterion_main};
#[cfg(not(any(
not(criterion),
not(feature = "fs"),
not(feature = "process"),
not(feature = "time"),
windows,
target_os = "emscripten",
target_os = "redox",
target_os = "wasi",
)))]
mod suite {
use criterion::Criterion;
pub(super) fn simple_statat(c: &mut Criterion) {
use rustix::fs::{cwd, statat, AtFlags};
c.bench_function("simple statat", |b| {
b.iter(|| {
statat(cwd(), "/", AtFlags::empty()).unwrap();
})
});
}
pub(super) fn simple_statat_libc(c: &mut Criterion) {
c.bench_function("simple statat libc", |b| {
b.iter(|| {
let mut s = std::mem::MaybeUninit::<libc::stat>::uninit();
unsafe {
assert_eq!(
libc::fstatat(
libc::AT_FDCWD,
std::ffi::CString::new("/").unwrap().as_c_str().as_ptr() as _,
s.as_mut_ptr(),
0
),
0
);
}
})
});
}
pub(super) fn simple_statat_libc_cstr(c: &mut Criterion) {
c.bench_function("simple statat libc cstr", |b| {
b.iter(|| {
let mut s = std::mem::MaybeUninit::<libc::stat>::uninit();
unsafe {
assert_eq!(
libc::fstatat(
libc::AT_FDCWD,
rustix::cstr!("/").as_ptr() as _,
s.as_mut_ptr(),
0
),
0
);
}
})
});
}
pub(super) fn simple_statat_cstr(c: &mut Criterion) {
use rustix::fs::{cwd, statat, AtFlags};
c.bench_function("simple statat cstr", |b| {
b.iter(|| {
statat(cwd(), rustix::cstr!("/"), AtFlags::empty()).unwrap();
})
});
}
#[cfg(not(target_os = "wasi"))]
pub(super) fn simple_clock_gettime(c: &mut Criterion) {
use rustix::time::{clock_gettime, ClockId};
c.bench_function("simple clock_gettime", |b| {
b.iter(|| {
let _ = clock_gettime(ClockId::Monotonic);
})
});
}
#[cfg(not(target_os = "wasi"))]
pub(super) fn simple_clock_gettime_libc(c: &mut Criterion) {
c.bench_function("simple clock_gettime libc", |b| {
b.iter(|| {
let mut s = std::mem::MaybeUninit::<libc::timespec>::uninit();
unsafe {
assert_eq!(
libc::clock_gettime(libc::CLOCK_MONOTONIC, s.as_mut_ptr()),
0
);
let _ = s.assume_init();
}
})
});
}
#[cfg(not(target_os = "wasi"))]
pub(super) fn simple_getpid(c: &mut Criterion) {
use rustix::process::getpid;
c.bench_function("simple getpid", |b| {
b.iter(|| {
let _ = getpid();
})
});
}
#[cfg(not(target_os = "wasi"))]
pub(super) fn simple_getpid_libc(c: &mut Criterion) {
c.bench_function("simple getpid libc", |b| {
b.iter(|| unsafe {
let _ = libc::getpid();
})
});
}
}
#[cfg(not(any(
not(criterion),
not(feature = "fs"),
not(feature = "process"),
not(feature = "time"),
windows,
target_os = "emscripten",
target_os = "redox",
target_os = "wasi",
)))]
criterion_group!(
benches,
suite::simple_statat,
suite::simple_statat_libc,
suite::simple_statat_libc_cstr,
suite::simple_statat_cstr,
suite::simple_clock_gettime,
suite::simple_clock_gettime_libc,
suite::simple_getpid,
suite::simple_getpid_libc
);
#[cfg(not(any(
not(criterion),
not(feature = "fs"),
not(feature = "process"),
not(feature = "time"),
windows,
target_os = "emscripten",
target_os = "redox",
target_os = "wasi",
)))]
criterion_main!(benches);
+20 -3
View File
@@ -26,7 +26,7 @@ fn main() {
let arch = var("CARGO_CFG_TARGET_ARCH").unwrap();
let asm_name = format!("{}/{}.s", OUTLINE_PATH, arch);
let asm_name_present = std::fs::metadata(&asm_name).is_ok();
let os_name = var("CARGO_CFG_TARGET_OS").unwrap();
let target_os = var("CARGO_CFG_TARGET_OS").unwrap();
let pointer_width = var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap();
let endian = var("CARGO_CFG_TARGET_ENDIAN").unwrap();
@@ -69,7 +69,7 @@ fn main() {
// install the toolchain for it.
if feature_use_libc
|| cfg_use_libc
|| os_name != "linux"
|| target_os != "linux"
|| !asm_name_present
|| is_unsupported_abi
|| miri
@@ -106,7 +106,17 @@ fn main() {
use_feature("thumb_mode");
}
if target_os == "wasi" {
use_feature_or_nothing("wasi_ext");
}
println!("cargo:rerun-if-env-changed=CARGO_CFG_RUSTIX_USE_EXPERIMENTAL_ASM");
println!("cargo:rerun-if-env-changed=CARGO_CFG_RUSTIX_USE_LIBC");
// Rerun this script if any of our features or configuration flags change,
// or if the toolchain we used for feature detection changes.
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_USE_LIBC");
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_RUSTC_DEP_OF_STD");
println!("cargo:rerun-if-env-changed=CARGO_CFG_MIRI");
}
/// Link in the desired version of librustix_outline_{arch}.a, containing the
@@ -187,7 +197,14 @@ fn can_compile<T: AsRef<str>>(test: T) -> bool {
let rustc = var("RUSTC").unwrap();
let target = var("TARGET").unwrap();
let mut cmd = if let Ok(wrapper) = var("CARGO_RUSTC_WRAPPER") {
// Use `RUSTC_WRAPPER` if it's set, unless it's set to an empty string,
// as documented [here].
// [here]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-reads
let wrapper = var("RUSTC_WRAPPER")
.ok()
.and_then(|w| if w.is_empty() { None } else { Some(w) });
let mut cmd = if let Some(wrapper) = wrapper {
let mut cmd = std::process::Command::new(wrapper);
// The wrapper's first argument is supposed to be the path to rustc.
cmd.arg(rustc);
+90 -12
View File
@@ -57,8 +57,13 @@ use core::ptr::NonNull;
use libc_errno::{errno, set_errno, Errno};
/// `DIR*`
#[repr(transparent)]
pub struct Dir(NonNull<c::DIR>);
pub struct Dir {
/// The `libc` `DIR` pointer.
libc_dir: NonNull<c::DIR>,
/// Have we seen any errors in this iteration?
any_errors: bool,
}
impl Dir {
/// Construct a `Dir` that reads entries from the given directory
@@ -69,21 +74,38 @@ impl Dir {
}
#[inline]
#[allow(unused_mut)]
fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> {
let mut any_errors = false;
// Given an arbitrary `OwnedFd`, it's impossible to know whether the
// user holds a `dup`'d copy which could continue to modify the
// file description state, which would cause Undefined Behavior after
// our call to `fdopendir`. To prevent this, we obtain an independent
// `OwnedFd`.
let flags = fcntl_getfl(fd)?;
let fd_for_dir = openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty())?;
let fd_for_dir = match openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty()) {
Ok(fd) => fd,
#[cfg(not(target_os = "wasi"))]
Err(io::Errno::NOENT) => {
// If "." doesn't exist, it means the directory was removed.
// We treat that as iterating through a directory with no
// entries.
any_errors = true;
crate::io::dup(fd)?
}
Err(err) => return Err(err),
};
let raw = owned_fd(fd_for_dir);
unsafe {
let libc_dir = c::fdopendir(raw);
if let Some(libc_dir) = NonNull::new(libc_dir) {
Ok(Self(libc_dir))
Ok(Self {
libc_dir,
any_errors,
})
} else {
let err = io::Errno::last_os_error();
let _ = c::close(raw);
@@ -95,13 +117,19 @@ impl Dir {
/// `rewinddir(self)`
#[inline]
pub fn rewind(&mut self) {
unsafe { c::rewinddir(self.0.as_ptr()) }
self.any_errors = false;
unsafe { c::rewinddir(self.libc_dir.as_ptr()) }
}
/// `readdir(self)`, where `None` means the end of the directory.
pub fn read(&mut self) -> Option<io::Result<DirEntry>> {
// If we've seen errors, don't continue to try to read anyting further.
if self.any_errors {
return None;
}
set_errno(Errno(0));
let dirent_ptr = unsafe { libc_readdir(self.0.as_ptr()) };
let dirent_ptr = unsafe { libc_readdir(self.libc_dir.as_ptr()) };
if dirent_ptr.is_null() {
let curr_errno = errno().0;
if curr_errno == 0 {
@@ -109,6 +137,7 @@ impl Dir {
None
} else {
// `errno` is unknown or non-zero, so an error occurred.
self.any_errors = true;
Some(Err(io::Errno(curr_errno)))
}
} else {
@@ -134,7 +163,7 @@ impl Dir {
/// `fstat(self)`
#[inline]
pub fn stat(&self) -> io::Result<Stat> {
fstat(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) })
fstat(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) })
}
/// `fstatfs(self)`
@@ -148,7 +177,7 @@ impl Dir {
)))]
#[inline]
pub fn statfs(&self) -> io::Result<StatFs> {
fstatfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) })
fstatfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) })
}
/// `fstatvfs(self)`
@@ -161,14 +190,14 @@ impl Dir {
)))]
#[inline]
pub fn statvfs(&self) -> io::Result<StatVfs> {
fstatvfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) })
fstatvfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) })
}
/// `fchdir(self)`
#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
#[inline]
pub fn chdir(&self) -> io::Result<()> {
fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) })
fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.libc_dir.as_ptr())) })
}
}
@@ -342,7 +371,7 @@ unsafe impl Send for Dir {}
impl Drop for Dir {
#[inline]
fn drop(&mut self) {
unsafe { c::closedir(self.0.as_ptr()) };
unsafe { c::closedir(self.libc_dir.as_ptr()) };
}
}
@@ -358,7 +387,7 @@ impl Iterator for Dir {
impl fmt::Debug for Dir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Dir")
.field("fd", unsafe { &c::dirfd(self.0.as_ptr()) })
.field("fd", unsafe { &c::dirfd(self.libc_dir.as_ptr()) })
.finish()
}
}
@@ -485,3 +514,52 @@ fn check_dirent_layout(dirent: &c::dirent) {
}
);
}
#[test]
fn dir_iterator_handles_io_errors() {
// create a dir, keep the FD, then delete the dir
let tmp = tempfile::tempdir().unwrap();
let fd = crate::fs::openat(
crate::fs::cwd(),
tmp.path(),
crate::fs::OFlags::RDONLY | crate::fs::OFlags::CLOEXEC,
crate::fs::Mode::empty(),
)
.unwrap();
let file_fd = crate::fs::openat(
&fd,
tmp.path().join("test.txt"),
crate::fs::OFlags::WRONLY | crate::fs::OFlags::CREATE,
crate::fs::Mode::RWXU,
)
.unwrap();
let mut dir = Dir::read_from(&fd).unwrap();
// Reach inside the `Dir` and replace its directory with a file, which
// will cause the subsequent `readdir` to fail.
unsafe {
let raw_fd = c::dirfd(dir.libc_dir.as_ptr());
let mut owned_fd: crate::fd::OwnedFd = crate::fd::FromRawFd::from_raw_fd(raw_fd);
crate::io::dup2(&file_fd, &mut owned_fd).unwrap();
core::mem::forget(owned_fd);
}
// FreeBSD and macOS seem to read some directory entries before we call
// `.next()`.
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "tvos",
target_os = "freebsd",
target_os = "dragonflybsd"
))]
{
dir.rewind();
}
assert!(matches!(dir.next(), Some(Err(_))));
assert!(matches!(dir.next(), None));
}
+7
View File
@@ -1485,13 +1485,20 @@ fn stat64_to_stat(s64: c::stat64) -> io::Result<Stat> {
mod sys {
use super::{c, BorrowedFd, Statx};
// Some versions of the libc bindings don't have these, so provide
// our own definitions (which may become unused when libc gains
// bindings for them).
#[cfg(all(target_os = "android", target_arch = "arm"))]
#[allow(dead_code)]
const SYS_statx: c::c_long = 397;
#[cfg(all(target_os = "android", target_arch = "x86"))]
#[allow(dead_code)]
const SYS_statx: c::c_long = 383;
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
#[allow(dead_code)]
const SYS_statx: c::c_long = 291;
#[cfg(all(target_os = "android", target_arch = "x86_64"))]
#[allow(dead_code)]
const SYS_statx: c::c_long = 332;
weak_or_syscall! {
+109 -14
View File
@@ -213,8 +213,21 @@ pub(super) use c::posix_fadvise64 as libc_posix_fadvise;
pub(super) use c::{pread as libc_pread, pwrite as libc_pwrite};
#[cfg(any(target_os = "android", target_os = "linux", target_os = "emscripten"))]
pub(super) use c::{pread64 as libc_pread, pwrite64 as libc_pwrite};
#[cfg(not(any(
windows,
target_os = "android",
target_os = "emscripten",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "redox",
target_os = "solaris",
)))]
pub(super) use c::{preadv as libc_preadv, pwritev as libc_pwritev};
#[cfg(any(target_os = "linux", target_os = "emscripten"))]
pub(super) use c::{preadv64 as libc_preadv, pwritev64 as libc_pwritev};
#[cfg(target_os = "android")]
mod readwrite_pv64 {
use super::c;
@@ -302,20 +315,9 @@ mod readwrite_pv64 {
}
}
}
#[cfg(not(any(
windows,
target_os = "android",
target_os = "emscripten",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "redox",
target_os = "solaris",
)))]
pub(super) use c::{preadv as libc_preadv, pwritev as libc_pwritev};
#[cfg(target_os = "android")]
pub(super) use readwrite_pv64::{preadv64 as libc_preadv, pwritev64 as libc_pwritev};
// macOS added preadv and pwritev in version 11.0
#[cfg(any(target_os = "ios", target_os = "macos"))]
mod readwrite_pv {
@@ -337,11 +339,104 @@ mod readwrite_pv {
) -> c::ssize_t
}
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub(super) use c::{preadv64v2 as libc_preadv2, pwritev64v2 as libc_pwritev2};
#[cfg(any(target_os = "ios", target_os = "macos"))]
pub(super) use readwrite_pv::{preadv as libc_preadv, pwritev as libc_pwritev};
// GLIBC added `preadv64v2` and `pwritev64v2` in version 2.26.
#[cfg(all(target_os = "linux", target_env = "gnu"))]
mod readwrite_pv64v2 {
use super::c;
// 64-bit offsets on 32-bit platforms are passed in endianness-specific
// lo/hi pairs. See src/backend/linux_raw/conv.rs for details.
#[cfg(all(target_endian = "little", target_pointer_width = "32"))]
fn lo(x: u64) -> usize {
(x >> 32) as usize
}
#[cfg(all(target_endian = "little", target_pointer_width = "32"))]
fn hi(x: u64) -> usize {
(x & 0xffff_ffff) as usize
}
#[cfg(all(target_endian = "big", target_pointer_width = "32"))]
fn lo(x: u64) -> usize {
(x & 0xffff_ffff) as usize
}
#[cfg(all(target_endian = "big", target_pointer_width = "32"))]
fn hi(x: u64) -> usize {
(x >> 32) as usize
}
pub(in super::super) unsafe fn preadv64v2(
fd: c::c_int,
iov: *const c::iovec,
iovcnt: c::c_int,
offset: c::off64_t,
flags: c::c_int,
) -> c::ssize_t {
// Older GLIBC lacks `preadv64v2`, so use the `weak!` mechanism to
// test for it, and call back to `c::syscall`. We don't use
// `weak_or_syscall` here because we need to pass the 64-bit offset
// specially.
weak! {
fn preadv64v2(c::c_int, *const c::iovec, c::c_int, c::off64_t, c::c_int) -> c::ssize_t
}
if let Some(fun) = preadv64v2.get() {
fun(fd, iov, iovcnt, offset, flags)
} else {
#[cfg(target_pointer_width = "32")]
{
c::syscall(
c::SYS_preadv,
fd,
iov,
iovcnt,
hi(offset as u64),
lo(offset as u64),
flags,
) as c::ssize_t
}
#[cfg(target_pointer_width = "64")]
{
c::syscall(c::SYS_preadv2, fd, iov, iovcnt, offset, flags) as c::ssize_t
}
}
}
pub(in super::super) unsafe fn pwritev64v2(
fd: c::c_int,
iov: *const c::iovec,
iovcnt: c::c_int,
offset: c::off64_t,
flags: c::c_int,
) -> c::ssize_t {
// See the comments in `preadv64v2`.
weak! {
fn pwritev64v2(c::c_int, *const c::iovec, c::c_int, c::off64_t, c::c_int) -> c::ssize_t
}
if let Some(fun) = pwritev64v2.get() {
fun(fd, iov, iovcnt, offset, flags)
} else {
#[cfg(target_pointer_width = "32")]
{
c::syscall(
c::SYS_pwritev,
fd,
iov,
iovcnt,
hi(offset as u64),
lo(offset as u64),
flags,
) as c::ssize_t
}
#[cfg(target_pointer_width = "64")]
{
c::syscall(c::SYS_pwritev2, fd, iov, iovcnt, offset, flags) as c::ssize_t
}
}
}
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub(super) use readwrite_pv64v2::{preadv64v2 as libc_preadv2, pwritev64v2 as libc_pwritev2};
#[cfg(not(any(
windows,
target_os = "aix",
+7 -1
View File
@@ -291,7 +291,13 @@ pub(crate) fn gettid() -> Pid {
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) fn setns(fd: BorrowedFd, nstype: c::c_int) -> io::Result<c::c_int> {
unsafe { ret_c_int(c::setns(borrowed_fd(fd), nstype)) }
// `setns` wasn't supported in glibc until 2.14, and musl until 0.9.5,
// so use `syscall`.
weak_or_syscall! {
fn setns(fd: c::c_int, nstype: c::c_int) via SYS_setns -> c::c_int
}
unsafe { ret_c_int(setns(borrowed_fd(fd), nstype)) }
}
#[cfg(any(target_os = "android", target_os = "linux"))]
+4 -8
View File
@@ -5,7 +5,10 @@
//! instruction.
//!
//! Most `rustix` syscalls use the vsyscall mechanism rather than going using
//! `int 0x80` sequences.
//! `int 0x80` sequences, as vsyscall is much faster.
//!
//! Syscalls made with `int 0x80` preserve the flags register, while syscalls
//! made using vsyscall do not.
#![allow(dead_code)]
@@ -25,7 +28,6 @@ pub(in crate::backend) unsafe fn indirect_syscall0(
"call {callee}",
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
@@ -42,7 +44,6 @@ pub(in crate::backend) unsafe fn indirect_syscall1(
callee = in(reg) callee,
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
@@ -76,7 +77,6 @@ pub(in crate::backend) unsafe fn indirect_syscall2(
inlateout("eax") nr.to_asm() => r0,
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
@@ -97,7 +97,6 @@ pub(in crate::backend) unsafe fn indirect_syscall3(
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
@@ -128,7 +127,6 @@ pub(in crate::backend) unsafe fn indirect_syscall4(
in("ebx") a0.to_asm(),
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
@@ -161,7 +159,6 @@ pub(in crate::backend) unsafe fn indirect_syscall5(
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
@@ -203,7 +200,6 @@ pub(in crate::backend) unsafe fn indirect_syscall6(
in("ecx") a1.to_asm(),
in("edx") a2.to_asm(),
in("edi") a4.to_asm(),
options(preserves_flags)
);
FromAsm::from_asm(r0)
}
+82 -13
View File
@@ -17,9 +17,17 @@ pub struct Dir {
/// The `OwnedFd` that we read directory entries from.
fd: OwnedFd,
/// Have we seen any errors in this iteration?
any_errors: bool,
/// Should we rewind the stream on the next iteration?
rewind: bool,
/// The buffer for `linux_dirent64` entries.
buf: Vec<u8>,
/// Where we are in the buffer.
pos: usize,
next: Option<u64>,
}
impl Dir {
@@ -37,25 +45,39 @@ impl Dir {
Ok(Self {
fd: fd_for_dir,
any_errors: false,
rewind: false,
buf: Vec::new(),
pos: 0,
next: None,
})
}
/// `rewinddir(self)`
#[inline]
pub fn rewind(&mut self) {
self.any_errors = false;
self.rewind = true;
self.pos = self.buf.len();
self.next = Some(0);
}
/// `readdir(self)`, where `None` means the end of the directory.
pub fn read(&mut self) -> Option<io::Result<DirEntry>> {
if let Some(next) = self.next.take() {
match crate::backend::fs::syscalls::_seek(self.fd.as_fd(), next as i64, SEEK_SET) {
// If we've seen errors, don't continue to try to read anyting further.
if self.any_errors {
return None;
}
// If a rewind was requested, seek to the beginning.
if self.rewind {
self.rewind = false;
match io::retry_on_intr(|| {
crate::backend::fs::syscalls::_seek(self.fd.as_fd(), 0, SEEK_SET)
}) {
Ok(_) => (),
Err(err) => return Some(Err(err)),
Err(err) => {
self.any_errors = true;
return Some(Err(err));
}
}
}
@@ -77,7 +99,7 @@ impl Dir {
if self.buf.len() - self.pos < size_of::<linux_dirent64>() {
match self.read_more()? {
Ok(()) => (),
Err(e) => return Some(Err(e)),
Err(err) => return Some(Err(err)),
}
}
@@ -136,14 +158,31 @@ impl Dir {
}
fn read_more(&mut self) -> Option<io::Result<()>> {
let og_len = self.buf.len();
// Capacity increment currently chosen by wild guess.
self.buf
.resize(self.buf.capacity() + 32 * size_of::<linux_dirent64>(), 0);
let nread = match crate::backend::fs::syscalls::getdents(self.fd.as_fd(), &mut self.buf) {
// The first few times we're called, we allocate a relatively small
// buffer, because many directories are small. If we're called more,
// use progressively larger allocations, up to a fixed maximum.
//
// The specific sizes and policy here have not been tuned in detail yet
// and may need to be adjusted. In doing so, we should be careful to
// avoid unbounded buffer growth. This buffer only exists to share the
// cost of a `getdents` call over many entries, so if it gets too big,
// cache and heap usage will outweigh the benefit. And ultimately,
// directories can contain more entries than we can allocate contiguous
// memory for, so we'll always need to cap the size at some point.
if self.buf.len() < 1024 * size_of::<linux_dirent64>() {
self.buf.reserve(32 * size_of::<linux_dirent64>());
}
self.buf.resize(self.buf.capacity(), 0);
let nread = match io::retry_on_intr(|| {
crate::backend::fs::syscalls::getdents(self.fd.as_fd(), &mut self.buf)
}) {
Ok(nread) => nread,
Err(io::Errno::NOENT) => {
self.any_errors = true;
return None;
}
Err(err) => {
self.buf.resize(og_len, 0);
self.any_errors = true;
return Some(Err(err));
}
};
@@ -223,3 +262,33 @@ impl DirEntry {
self.d_ino
}
}
#[test]
fn dir_iterator_handles_io_errors() {
// create a dir, keep the FD, then delete the dir
let tmp = tempfile::tempdir().unwrap();
let fd = crate::fs::openat(
crate::fs::cwd(),
tmp.path(),
crate::fs::OFlags::RDONLY | crate::fs::OFlags::CLOEXEC,
crate::fs::Mode::empty(),
)
.unwrap();
let file_fd = crate::fs::openat(
&fd,
tmp.path().join("test.txt"),
crate::fs::OFlags::WRONLY | crate::fs::OFlags::CREATE,
crate::fs::Mode::RWXU,
)
.unwrap();
let mut dir = Dir::read_from(&fd).unwrap();
// Reach inside the `Dir` and replace its directory with a file, which
// will cause the subsequent `getdents64` to fail.
crate::io::dup2(&file_fd, &mut dir.fd).unwrap();
assert!(matches!(dir.next(), Some(Err(_))));
assert!(matches!(dir.next(), None));
}
+5
View File
@@ -14,6 +14,11 @@
//! such as which pointers are array slices, out parameters, or in-out
//! parameters, which integers are owned or borrowed file descriptors, etc.
// Weak symbols used by the use-libc-auxv feature for glibc 2.15 support.
#[cfg(feature = "use-libc-auxv")]
#[macro_use]
mod weak;
#[macro_use]
mod arch;
mod conv;
+23 -14
View File
@@ -5,14 +5,15 @@
//! This uses raw pointers to locate and read the kernel-provided auxv array.
#![allow(unsafe_code)]
#[cfg(any(feature = "param", feature = "runtime"))]
use super::super::c;
use super::super::elf::*;
#[cfg(feature = "param")]
use crate::ffi::CStr;
#[cfg(feature = "runtime")]
use core::slice;
// `getauxval` wasn't supported in glibc until 2.16.
weak!(fn getauxval(libc::c_ulong) -> *mut libc::c_void);
#[cfg(feature = "param")]
#[inline]
pub(crate) fn page_size() -> usize {
@@ -22,35 +23,39 @@ pub(crate) fn page_size() -> usize {
#[cfg(feature = "param")]
#[inline]
pub(crate) fn clock_ticks_per_second() -> u64 {
unsafe { libc::getauxval(libc::AT_CLKTCK) as u64 }
unsafe { libc::sysconf(libc::_SC_CLK_TCK) as u64 }
}
#[cfg(feature = "param")]
#[inline]
pub(crate) fn linux_hwcap() -> (usize, usize) {
unsafe {
(
libc::getauxval(libc::AT_HWCAP) as usize,
libc::getauxval(libc::AT_HWCAP2) as usize,
)
if let Some(libc_getauxval) = getauxval.get() {
unsafe {
let hwcap = libc_getauxval(libc::AT_HWCAP) as usize;
let hwcap2 = libc_getauxval(libc::AT_HWCAP2) as usize;
(hwcap, hwcap2)
}
} else {
(0, 0)
}
}
#[cfg(feature = "param")]
#[inline]
pub(crate) fn linux_execfn() -> &'static CStr {
unsafe {
let execfn = libc::getauxval(libc::AT_EXECFN) as *const c::c_char;
CStr::from_ptr(execfn.cast())
if let Some(libc_getauxval) = getauxval.get() {
unsafe { CStr::from_ptr(libc_getauxval(libc::AT_EXECFN).cast()) }
} else {
cstr!("")
}
}
#[cfg(feature = "runtime")]
#[inline]
pub(crate) fn exe_phdrs() -> (*const c::c_void, usize) {
pub(crate) fn exe_phdrs() -> (*const libc::c_void, usize) {
unsafe {
(
libc::getauxval(libc::AT_PHDR) as *const c::c_void,
libc::getauxval(libc::AT_PHDR) as *const libc::c_void,
libc::getauxval(libc::AT_PHNUM) as usize,
)
}
@@ -70,5 +75,9 @@ pub(in super::super) fn exe_phdrs_slice() -> &'static [Elf_Phdr] {
/// so if we don't see it, this function returns a null pointer.
#[inline]
pub(in super::super) fn sysinfo_ehdr() -> *const Elf_Ehdr {
unsafe { libc::getauxval(linux_raw_sys::general::AT_SYSINFO_EHDR.into()) as *const Elf_Ehdr }
if let Some(libc_getauxval) = getauxval.get() {
unsafe { libc_getauxval(linux_raw_sys::general::AT_SYSINFO_EHDR.into()) as *const Elf_Ehdr }
} else {
core::ptr::null()
}
}
+228
View File
@@ -0,0 +1,228 @@
// Implementation derived from `weak` in Rust's
// library/std/src/sys/unix/weak.rs at revision
// fd0cb0cdc21dd9c06025277d772108f8d42cb25f.
#![allow(unsafe_code)]
//! Support for "weak linkage" to symbols on Unix
//!
//! Some I/O operations we do in libstd require newer versions of OSes but we
//! need to maintain binary compatibility with older releases for now. In order
//! to use the new functionality when available we use this module for
//! detection.
//!
//! One option to use here is weak linkage, but that is unfortunately only
//! really workable on Linux. Hence, use dlsym to get the symbol value at
//! runtime. This is also done for compatibility with older versions of glibc,
//! and to avoid creating dependencies on `GLIBC_PRIVATE` symbols. It assumes
//! that we've been dynamically linked to the library the symbol comes from,
//! but that is currently always the case for things like libpthread/libc.
//!
//! A long time ago this used weak linkage for the `__pthread_get_minstack`
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
//! dependency on libc6 (#23628).
// There are a variety of `#[cfg]`s controlling which targets are involved in
// each instance of `weak!` and `syscall!`. Rather than trying to unify all of
// that, we'll just allow that some unix targets don't use this module at all.
#![allow(dead_code, unused_macros)]
#![allow(clippy::doc_markdown)]
use crate::ffi::CStr;
use core::ffi::c_void;
use core::ptr::null_mut;
use core::sync::atomic::{self, AtomicPtr, Ordering};
use core::{marker, mem};
const NULL: *mut c_void = null_mut();
const INVALID: *mut c_void = 1 as *mut c_void;
macro_rules! weak {
($vis:vis fn $name:ident($($t:ty),*) -> $ret:ty) => (
#[allow(non_upper_case_globals)]
$vis static $name: $crate::backend::weak::Weak<unsafe extern fn($($t),*) -> $ret> =
$crate::backend::weak::Weak::new(concat!(stringify!($name), '\0'));
)
}
pub(crate) struct Weak<F> {
name: &'static str,
addr: AtomicPtr<c_void>,
_marker: marker::PhantomData<F>,
}
impl<F> Weak<F> {
pub(crate) const fn new(name: &'static str) -> Self {
Self {
name,
addr: AtomicPtr::new(INVALID),
_marker: marker::PhantomData,
}
}
pub(crate) fn get(&self) -> Option<F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
unsafe {
// Relaxed is fine here because we fence before reading through the
// pointer (see the comment below).
match self.addr.load(Ordering::Relaxed) {
INVALID => self.initialize(),
NULL => None,
addr => {
let func = mem::transmute_copy::<*mut c_void, F>(&addr);
// The caller is presumably going to read through this value
// (by calling the function we've dlsymed). This means we'd
// need to have loaded it with at least C11's consume
// ordering in order to be guaranteed that the data we read
// from the pointer isn't from before the pointer was
// stored. Rust has no equivalent to memory_order_consume,
// so we use an acquire fence (sorry, ARM).
//
// Now, in practice this likely isn't needed even on CPUs
// where relaxed and consume mean different things. The
// symbols we're loading are probably present (or not) at
// init, and even if they aren't the runtime dynamic loader
// is extremely likely have sufficient barriers internally
// (possibly implicitly, for example the ones provided by
// invoking `mprotect`).
//
// That said, none of that's *guaranteed*, and so we fence.
atomic::fence(Ordering::Acquire);
Some(func)
}
}
}
}
// Cold because it should only happen during first-time initialization.
#[cold]
unsafe fn initialize(&self) -> Option<F> {
let val = fetch(self.name);
// This synchronizes with the acquire fence in `get`.
self.addr.store(val, Ordering::Release);
match val {
NULL => None,
addr => Some(mem::transmute_copy::<*mut c_void, F>(&addr)),
}
}
}
unsafe fn fetch(name: &str) -> *mut c_void {
let name = match CStr::from_bytes_with_nul(name.as_bytes()) {
Ok(c_str) => c_str,
Err(..) => return null_mut(),
};
libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr().cast())
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
macro_rules! syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) via $_sys_name:ident -> $ret:ty) => (
unsafe fn $name($($arg_name: $t),*) -> $ret {
weak! { fn $name($($t),*) -> $ret }
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
libc_errno::set_errno(libc_errno::Errno(libc::ENOSYS));
-1
}
}
)
}
#[cfg(any(target_os = "android", target_os = "linux"))]
macro_rules! syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) via $sys_name:ident -> $ret:ty) => (
unsafe fn $name($($arg_name:$t),*) -> $ret {
// This looks like a hack, but concat_idents only accepts idents
// (not paths).
use libc::*;
trait AsSyscallArg {
type SyscallArgType;
fn into_syscall_arg(self) -> Self::SyscallArgType;
}
// Pass pointer types as pointers, to preserve provenance.
impl<T> AsSyscallArg for *mut T {
type SyscallArgType = *mut T;
fn into_syscall_arg(self) -> Self::SyscallArgType { self }
}
impl<T> AsSyscallArg for *const T {
type SyscallArgType = *const T;
fn into_syscall_arg(self) -> Self::SyscallArgType { self }
}
// Pass `BorrowedFd` values as the integer value.
impl AsSyscallArg for $crate::fd::BorrowedFd<'_> {
type SyscallArgType = c::c_long;
fn into_syscall_arg(self) -> Self::SyscallArgType {
$crate::fd::AsRawFd::as_raw_fd(&self) as _
}
}
// Coerce integer values into `c_long`.
impl AsSyscallArg for i32 {
type SyscallArgType = c::c_long;
fn into_syscall_arg(self) -> Self::SyscallArgType { self as _ }
}
impl AsSyscallArg for u32 {
type SyscallArgType = c::c_long;
fn into_syscall_arg(self) -> Self::SyscallArgType { self as _ }
}
impl AsSyscallArg for usize {
type SyscallArgType = c::c_long;
fn into_syscall_arg(self) -> Self::SyscallArgType { self as _ }
}
// `concat_idents is unstable, so we take an extra `sys_name`
// parameter and have our users do the concat for us for now.
/*
syscall(
concat_idents!(SYS_, $name),
$($arg_name.into_syscall_arg()),*
) as $ret
*/
syscall($sys_name, $($arg_name.into_syscall_arg()),*) as $ret
}
)
}
macro_rules! weakcall {
($vis:vis fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
$vis unsafe fn $name($($arg_name: $t),*) -> $ret {
weak! { fn $name($($t),*) -> $ret }
// Use a weak symbol from libc when possible, allowing `LD_PRELOAD`
// interposition, but if it's not found just fail.
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
libc_errno::set_errno(libc_errno::Errno(libc::ENOSYS));
-1
}
}
)
}
/// A combination of `weakcall` and `syscall`. Use the libc function if it's
/// available, and fall back to `libc::syscall` otherwise.
macro_rules! weak_or_syscall {
($vis:vis fn $name:ident($($arg_name:ident: $t:ty),*) via $sys_name:ident -> $ret:ty) => (
$vis unsafe fn $name($($arg_name: $t),*) -> $ret {
weak! { fn $name($($t),*) -> $ret }
// Use a weak symbol from libc when possible, allowing `LD_PRELOAD`
// interposition, but if it's not found just fail.
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
syscall! { fn $name($($arg_name: $t),*) via $sys_name -> $ret }
$name($($arg_name),*)
}
}
)
}
+1 -4
View File
@@ -3,7 +3,7 @@
use crate::backend;
pub use crate::io::FdFlags;
pub use backend::fs::types::{Access, Mode, OFlags};
pub use backend::fs::types::{Access, Dev, Mode, OFlags};
#[cfg(not(target_os = "redox"))]
pub use backend::fs::types::AtFlags;
@@ -14,7 +14,4 @@ pub use backend::fs::types::{CloneFlags, CopyfileFlags};
#[cfg(any(target_os = "android", target_os = "linux"))]
pub use backend::fs::types::{MountFlags, MountPropagationFlags, RenameFlags, ResolveFlags};
#[cfg(not(target_os = "redox"))]
pub use backend::fs::types::Dev;
pub use backend::time::types::{Nsecs, Secs, Timespec};
+1 -1
View File
@@ -217,5 +217,5 @@ pub use statx::{statx, Statx, StatxFlags, StatxTimestamp};
#[cfg(unix)]
pub use std::os::unix::fs::{DirEntryExt, FileExt, FileTypeExt, MetadataExt, OpenOptionsExt};
#[cfg(feature = "std")]
#[cfg(target_os = "wasi")]
#[cfg(all(wasi_ext, target_os = "wasi"))]
pub use std::os::wasi::fs::{DirEntryExt, FileExt, FileTypeExt, MetadataExt, OpenOptionsExt};
+2 -2
View File
@@ -195,8 +195,8 @@ impl<'buf, Fd: AsFd> RawDir<'buf, Fd> {
Some(Ok(RawDirEntry {
file_type: dirent.d_type,
inode_number: dirent.d_ino,
next_entry_cookie: dirent.d_off,
inode_number: dirent.d_ino.into(),
next_entry_cookie: dirent.d_off.into(),
// SAFETY: the kernel guarantees a NUL terminated string.
file_name: unsafe { CStr::from_ptr(dirent.d_name.as_ptr().cast()) },
}))
-1
View File
@@ -11,7 +11,6 @@ pub use backend::io::io_slice::{IoSlice, IoSliceMut};
#[cfg(feature = "std")]
pub use std::io::{IoSlice, IoSliceMut};
/// `RWF_*` constants for use with [`preadv2`] and [`pwritev2`].
#[cfg(any(target_os = "android", target_os = "linux"))]
pub use backend::io::types::ReadWriteFlags;
+4 -1
View File
@@ -98,7 +98,7 @@
#![cfg_attr(linux_raw, deny(unsafe_code))]
#![cfg_attr(rustc_attrs, feature(rustc_attrs))]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![cfg_attr(all(target_os = "wasi", feature = "std"), feature(wasi_ext))]
#![cfg_attr(all(wasi_ext, target_os = "wasi", feature = "std"), feature(wasi_ext))]
#![cfg_attr(
all(linux_raw, naked_functions, target_arch = "x86"),
feature(naked_functions)
@@ -123,6 +123,9 @@
#![allow(clippy::unnecessary_cast)]
// It is common in linux and libc APIs for types to vary between platforms.
#![allow(clippy::useless_conversion)]
// Redox and WASI have enough differences that it isn't worth
// precisely conditionallizing all the `use`s for them.
#![cfg_attr(any(target_os = "redox", target_os = "wasi"), allow(unused_imports))]
#[cfg(not(feature = "rustc-dep-of-std"))]
extern crate alloc;
+17 -1
View File
@@ -2,6 +2,13 @@ use std::process::Command;
#[test]
fn test_backends() {
// Test whether `has_dependency` works. `cargo tree` no longer works in
// Rust 1.48 because `cargo tree` pulls in dependencies for all targets,
// and hermit-core isn't compatible with Rust 1.48.
if !has_dependency(".", &[], &[], &[], "io-lifetimes") {
return;
}
// Pick an arbitrary platform where linux_raw is enabled by default and
// ensure that the use-default crate uses it.
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
@@ -51,9 +58,18 @@ fn test_backends() {
#[cfg(windows)]
let libc_dep = "windows-sys";
#[cfg(unix)]
#[cfg(any(unix, target_os = "wasi"))]
let libc_dep = "libc";
// FIXME: Temporarily disable the subsequent tests on Windows until
// rust-errno updates to windows-sys 0.48.
#[cfg(windows)]
{
if true {
return;
}
}
// Test the use-libc crate, which enables the "use-libc" cargo feature.
assert!(
has_dependency("test-crates/use-libc", &[], &[], &["RUSTFLAGS"], libc_dep),
+93 -1
View File
@@ -8,7 +8,7 @@ fn test_dir() {
)
.unwrap();
let dir = rustix::fs::Dir::read_from(&t).unwrap();
let mut dir = rustix::fs::Dir::read_from(&t).unwrap();
let _file = rustix::fs::openat(
&t,
@@ -18,6 +18,32 @@ fn test_dir() {
)
.unwrap();
// Read the directory entries. We use `while let Some(entry)` so that we
// don't consume the `Dir` so that we can run more tests on it.
let mut saw_dot = false;
let mut saw_dotdot = false;
let mut saw_cargo_toml = false;
while let Some(entry) = dir.read() {
let entry = entry.unwrap();
if entry.file_name() == rustix::cstr!(".") {
saw_dot = true;
} else if entry.file_name() == rustix::cstr!("..") {
saw_dotdot = true;
} else if entry.file_name() == rustix::cstr!("Cargo.toml") {
saw_cargo_toml = true;
}
}
assert!(saw_dot);
assert!(saw_dotdot);
assert!(saw_cargo_toml);
// Rewind the directory so we can iterate over the entries again.
dir.rewind();
// For what comes next, we don't need `mut` anymore.
let dir = dir;
// Read the directory entries, again. This time we use `for entry in dir`.
let mut saw_dot = false;
let mut saw_dotdot = false;
let mut saw_cargo_toml = false;
@@ -35,3 +61,69 @@ fn test_dir() {
assert!(saw_dotdot);
assert!(saw_cargo_toml);
}
// Test that `Dir` silently stops iterating if the directory has been removed.
//
// Except on FreeBSD and macOS, where apparently `readdir` just keeps reading.
#[cfg_attr(
any(
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "tvos",
target_os = "freebsd",
target_os = "dragonflybsd"
),
ignore
)]
#[test]
fn dir_iterator_handles_dir_removal() {
// create a dir, keep the FD, then delete the dir
let tmp = tempfile::tempdir().unwrap();
let fd = rustix::fs::openat(
rustix::fs::cwd(),
tmp.path(),
rustix::fs::OFlags::RDONLY | rustix::fs::OFlags::CLOEXEC,
rustix::fs::Mode::empty(),
)
.unwrap();
// Drop the `TempDir`, which deletes the directory.
drop(tmp);
let mut dir = rustix::fs::Dir::read_from(&fd).unwrap();
assert!(matches!(dir.next(), None));
}
// Like `dir_iterator_handles_dir_removal`, but close the directory after
// `Dir::read_from`.
#[cfg_attr(
any(
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "tvos",
target_os = "freebsd",
target_os = "dragonflybsd"
),
ignore
)]
#[test]
fn dir_iterator_handles_dir_removal_after_open() {
// create a dir, keep the FD, then delete the dir
let tmp = tempfile::tempdir().unwrap();
let fd = rustix::fs::openat(
rustix::fs::cwd(),
tmp.path(),
rustix::fs::OFlags::RDONLY | rustix::fs::OFlags::CLOEXEC,
rustix::fs::Mode::empty(),
)
.unwrap();
let mut dir = rustix::fs::Dir::read_from(&fd).unwrap();
// Drop the `TempDir`, which deletes the directory.
drop(tmp);
assert!(matches!(dir.next(), None));
}
+1 -2
View File
@@ -1,6 +1,5 @@
use libc::{kill, SIGSTOP};
use rustix::process;
use serial_test::serial;
use std::process::{Command, Stdio};
// These tests must execute serially to prevent race condition, where
@@ -8,7 +7,7 @@ use std::process::{Command, Stdio};
// the tests to get stuck.
#[test]
#[serial]
#[ignore]
fn test_waitpid() {
let child = Command::new("yes")
.stdout(Stdio::null())
+5 -1
View File
@@ -4,7 +4,11 @@ use std::fs::File;
#[test]
fn test_ttyname_ok() {
let file = File::open("/dev/stdin").unwrap();
let file = match File::open("/dev/stdin") {
Ok(file) => file,
Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => return,
Err(err) => Err(err).unwrap(),
};
if isatty(&file) {
assert!(ttyname(&file, Vec::new())
.unwrap()