Skip to content

Commit 60f2290

Browse files
committed
Fix the mount tests
As originally written by @kamalmarhubi in #231, these tests made clever use of Linux namespaces in order to run as unprivileged users. However, a subsequent kernel bug broke this functionality, and it hasn't been fixed even 6 years later. The tests have been skipped ever since. Get the tests to run again by removing the namespace stuff and requiring privileges instead. Also, remove the custom test harness. Now Nix will be compatible with tools like cargo-nextest. https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1659087
1 parent 1acc5e8 commit 60f2290

File tree

2 files changed

+19
-100
lines changed

2 files changed

+19
-100
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ path = "test/test_clearenv.rs"
102102
[[test]]
103103
name = "test-mount"
104104
path = "test/test_mount.rs"
105-
harness = false
106105

107106
[[test]]
108107
name = "test-prctl"

test/test_mount.rs

Lines changed: 19 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,32 @@
1+
#[macro_use]
12
mod common;
23

3-
// Implementation note: to allow unprivileged users to run it, this test makes
4-
// use of user and mount namespaces. On systems that allow unprivileged user
5-
// namespaces (Linux >= 3.8 compiled with CONFIG_USER_NS), the test should run
6-
// without root.
7-
84
#[cfg(target_os = "linux")]
95
mod test_mount {
106
use std::fs::{self, File};
11-
use std::io::{self, Read, Write};
7+
use std::io::{Read, Write};
128
use std::os::unix::fs::OpenOptionsExt;
139
use std::os::unix::fs::PermissionsExt;
14-
use std::process::{self, Command};
10+
use std::process::Command;
1511

1612
use libc::{EACCES, EROFS};
1713

18-
use nix::errno::Errno;
1914
use nix::mount::{mount, umount, MsFlags};
20-
use nix::sched::{unshare, CloneFlags};
2115
use nix::sys::stat::{self, Mode};
22-
use nix::unistd::getuid;
2316

2417
static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh
2518
exit 23";
2619

2720
const EXPECTED_STATUS: i32 = 23;
2821

2922
const NONE: Option<&'static [u8]> = None;
30-
#[allow(clippy::bind_instead_of_map)] // False positive
31-
pub fn test_mount_tmpfs_without_flags_allows_rwx() {
23+
24+
#[test]
25+
fn test_mount_tmpfs_without_flags_allows_rwx() {
26+
require_capability!(
27+
"test_mount_tmpfs_without_flags_allows_rwx",
28+
CAP_SYS_ADMIN
29+
);
3230
let tempdir = tempfile::tempdir().unwrap();
3331

3432
mount(
@@ -48,28 +46,6 @@ exit 23";
4846
.write(true)
4947
.mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
5048
.open(&test_path)
51-
.or_else(|e| {
52-
if Errno::from_raw(e.raw_os_error().unwrap())
53-
== Errno::EOVERFLOW
54-
{
55-
// Skip tests on certain Linux kernels which have a bug
56-
// regarding tmpfs in namespaces.
57-
// Ubuntu 14.04 and 16.04 are known to be affected; 16.10 is
58-
// not. There is no legitimate reason for open(2) to return
59-
// EOVERFLOW here.
60-
// https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1659087
61-
let stderr = io::stderr();
62-
let mut handle = stderr.lock();
63-
writeln!(
64-
handle,
65-
"Buggy Linux kernel detected. Skipping test."
66-
)
67-
.unwrap();
68-
process::exit(0);
69-
} else {
70-
panic!("open failed: {e}");
71-
}
72-
})
7349
.and_then(|mut f| f.write(SCRIPT_CONTENTS))
7450
.unwrap_or_else(|e| panic!("write failed: {e}"));
7551

@@ -93,7 +69,9 @@ exit 23";
9369
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
9470
}
9571

96-
pub fn test_mount_rdonly_disallows_write() {
72+
#[test]
73+
fn test_mount_rdonly_disallows_write() {
74+
require_capability!("test_mount_rdonly_disallows_write", CAP_SYS_ADMIN);
9775
let tempdir = tempfile::tempdir().unwrap();
9876

9977
mount(
@@ -117,7 +95,9 @@ exit 23";
11795
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
11896
}
11997

120-
pub fn test_mount_noexec_disallows_exec() {
98+
#[test]
99+
fn test_mount_noexec_disallows_exec() {
100+
require_capability!("test_mount_noexec_disallows_exec", CAP_SYS_ADMIN);
121101
let tempdir = tempfile::tempdir().unwrap();
122102

123103
mount(
@@ -165,7 +145,9 @@ exit 23";
165145
umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
166146
}
167147

168-
pub fn test_mount_bind() {
148+
#[test]
149+
fn test_mount_bind() {
150+
require_capability!("test_mount_bind", CAP_SYS_ADMIN);
169151
let tempdir = tempfile::tempdir().unwrap();
170152
let file_name = "test";
171153

@@ -202,66 +184,4 @@ exit 23";
202184
.unwrap_or_else(|e| panic!("read failed: {e}"));
203185
assert_eq!(buf, SCRIPT_CONTENTS);
204186
}
205-
206-
pub fn setup_namespaces() {
207-
// Hold on to the uid in the parent namespace.
208-
let uid = getuid();
209-
210-
unshare(CloneFlags::CLONE_NEWNS | CloneFlags::CLONE_NEWUSER).unwrap_or_else(|e| {
211-
let stderr = io::stderr();
212-
let mut handle = stderr.lock();
213-
writeln!(handle,
214-
"unshare failed: {e}. Are unprivileged user namespaces available?").unwrap();
215-
writeln!(handle, "mount is not being tested").unwrap();
216-
// Exit with success because not all systems support unprivileged user namespaces, and
217-
// that's not what we're testing for.
218-
process::exit(0);
219-
});
220-
221-
// Map user as uid 1000.
222-
fs::OpenOptions::new()
223-
.write(true)
224-
.open("/proc/self/uid_map")
225-
.and_then(|mut f| f.write(format!("1000 {uid} 1\n").as_bytes()))
226-
.unwrap_or_else(|e| panic!("could not write uid map: {e}"));
227-
}
228187
}
229-
230-
// Test runner
231-
232-
/// Mimic normal test output (hackishly).
233-
#[cfg(target_os = "linux")]
234-
macro_rules! run_tests {
235-
( $($test_fn:ident),* ) => {{
236-
println!();
237-
238-
$(
239-
print!("test test_mount::{} ... ", stringify!($test_fn));
240-
$test_fn();
241-
println!("ok");
242-
)*
243-
244-
println!();
245-
}}
246-
}
247-
248-
#[cfg(target_os = "linux")]
249-
fn main() {
250-
use test_mount::{
251-
setup_namespaces, test_mount_bind, test_mount_noexec_disallows_exec,
252-
test_mount_rdonly_disallows_write,
253-
test_mount_tmpfs_without_flags_allows_rwx,
254-
};
255-
skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1351");
256-
setup_namespaces();
257-
258-
run_tests!(
259-
test_mount_tmpfs_without_flags_allows_rwx,
260-
test_mount_rdonly_disallows_write,
261-
test_mount_noexec_disallows_exec,
262-
test_mount_bind
263-
);
264-
}
265-
266-
#[cfg(not(target_os = "linux"))]
267-
fn main() {}

0 commit comments

Comments
 (0)