mirror of
https://github.com/uutils/coreutils.git
synced 2025-12-23 08:47:37 +00:00
tr: Fix regression causing read error with sockets (#8083)
* tr: Fix regression causing read error with sockets
The test used for determining if a file descriptor
tested with fstat points to a directory is traditionally
done by using the S_IFMT mask[^1][^2], e.g.:
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
In Rust, this translates to 'm & libc::S_IFMT == libc::S_IFDIR'.
is_stdin_directory() uses 'has!(mode, S_IFDIR)', which is
**not** equivalent and causes non-directory sockets to be
incorrectly recognized as directories. This causes uu-tr
to break when used with all sockets created with socketpair,
including ksh93 pipes[^3]. Below is an example Rust program
demonstrating why the current check is bogus:
fn main() {
use nix::sys::socket::{socketpair, SockFlag, AddressFamily, SockType};
use nix::sys::stat::fstat;
use libc::{S_IFDIR, S_IFMT, mode_t};
let (_fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::empty()).unwrap();
let mode = fstat(&fd2).unwrap().st_mode as mode_t;
if mode & S_IFMT == S_IFDIR { // Equivalent to S_ISDIR()
println!("Not reached"); // Not a dir, so unreachable
}
if mode & S_IFDIR == S_IFDIR { // Bogus check for S_IFDIR
println!("BAD");
}
}
- is_stdin_directory(): Fix the regression introduced in 3e4221a4
by replacing the bogus check with one equivalent to S_ISDIR().
- test_tr.rs: Add a regression test for this bug.
[^1]: https://sourceware.org/git/?p=glibc.git;a=blob;f=io/sys/stat.h;h=4bea9e9a#l123
[^2]: https://git.musl-libc.org/cgit/musl/tree/include/sys/stat.h?id=047a1639#n51
[^3]: cc5e0692/src/cmd/ksh93/sh/io.c (L98-L102)
Fixes https://github.com/uutils/coreutils/issues/7658
* Add comment explaining rationale
Also fix cargo clippy lint.
(In case it isn't obvious, I'm fairly new to Rust)
* Fix more lint
This commit is contained in:
parent
1d85566779
commit
6d30b9110a
5 changed files with 43 additions and 2 deletions
|
|
@ -154,6 +154,7 @@ IFSOCK
|
|||
IRGRP
|
||||
IROTH
|
||||
IRUSR
|
||||
ISDIR
|
||||
ISGID
|
||||
ISUID
|
||||
ISVTX
|
||||
|
|
@ -205,6 +206,7 @@ setgid
|
|||
setgroups
|
||||
settime
|
||||
setuid
|
||||
socketpair
|
||||
socktype
|
||||
statfs
|
||||
statp
|
||||
|
|
|
|||
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -1692,6 +1692,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
|
|
@ -1729,6 +1738,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -537,7 +537,13 @@ hex-literal = "1.0.0"
|
|||
rstest.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
nix = { workspace = true, features = ["process", "signal", "user", "term"] }
|
||||
nix = { workspace = true, features = [
|
||||
"process",
|
||||
"signal",
|
||||
"socket",
|
||||
"user",
|
||||
"term",
|
||||
] }
|
||||
rlimit = "0.10.1"
|
||||
xattr.workspace = true
|
||||
|
||||
|
|
|
|||
|
|
@ -720,7 +720,9 @@ pub fn is_stdin_directory(stdin: &Stdin) -> bool {
|
|||
{
|
||||
use nix::sys::stat::fstat;
|
||||
let mode = fstat(stdin.as_fd()).unwrap().st_mode as mode_t;
|
||||
has!(mode, S_IFDIR)
|
||||
// We use the S_IFMT mask ala S_ISDIR() to avoid mistaking
|
||||
// sockets for directories.
|
||||
mode & S_IFMT == S_IFDIR
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
|
|
|||
|
|
@ -1563,3 +1563,24 @@ fn test_broken_pipe_no_error() {
|
|||
.run_stdout_starts_with(b"")
|
||||
.fails_silently();
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn test_stdin_is_socket() {
|
||||
use nix::sys::socket::{AddressFamily, SockFlag, SockType, socketpair};
|
||||
use nix::unistd::write;
|
||||
|
||||
let (fd1, fd2) = socketpair(
|
||||
AddressFamily::Unix,
|
||||
SockType::Stream,
|
||||
None,
|
||||
SockFlag::empty(),
|
||||
)
|
||||
.unwrap();
|
||||
write(fd1, b"::").unwrap();
|
||||
new_ucmd!()
|
||||
.args(&[":", ";"])
|
||||
.set_stdin(fd2)
|
||||
.succeeds()
|
||||
.stdout_is(";;");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue