• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::fs::{self, File};
2 use std::io::{Read, Write};
3 use std::os::unix::fs::OpenOptionsExt;
4 use std::os::unix::fs::PermissionsExt;
5 use std::process::Command;
6 
7 use libc::{EACCES, EROFS};
8 
9 use nix::mount::{mount, umount, MsFlags};
10 use nix::sys::stat::{self, Mode};
11 
12 use crate::*;
13 
14 static SCRIPT_CONTENTS: &[u8] = b"#!/bin/sh
15 exit 23";
16 
17 const EXPECTED_STATUS: i32 = 23;
18 
19 const NONE: Option<&'static [u8]> = None;
20 
21 #[test]
test_mount_tmpfs_without_flags_allows_rwx()22 fn test_mount_tmpfs_without_flags_allows_rwx() {
23     require_capability!(
24         "test_mount_tmpfs_without_flags_allows_rwx",
25         CAP_SYS_ADMIN
26     );
27     let tempdir = tempfile::tempdir().unwrap();
28 
29     mount(
30         NONE,
31         tempdir.path(),
32         Some(b"tmpfs".as_ref()),
33         MsFlags::empty(),
34         NONE,
35     )
36     .unwrap_or_else(|e| panic!("mount failed: {e}"));
37 
38     let test_path = tempdir.path().join("test");
39 
40     // Verify write.
41     fs::OpenOptions::new()
42         .create(true)
43         .write(true)
44         .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
45         .open(&test_path)
46         .and_then(|mut f| f.write(SCRIPT_CONTENTS))
47         .unwrap_or_else(|e| panic!("write failed: {e}"));
48 
49     // Verify read.
50     let mut buf = Vec::new();
51     File::open(&test_path)
52         .and_then(|mut f| f.read_to_end(&mut buf))
53         .unwrap_or_else(|e| panic!("read failed: {e}"));
54     assert_eq!(buf, SCRIPT_CONTENTS);
55 
56     // while forking and unmounting prevent other child processes
57     let _m = FORK_MTX.lock();
58     // Verify execute.
59     assert_eq!(
60         EXPECTED_STATUS,
61         Command::new(&test_path)
62             .status()
63             .unwrap_or_else(|e| panic!("exec failed: {e}"))
64             .code()
65             .unwrap_or_else(|| panic!("child killed by signal"))
66     );
67 
68     umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
69 }
70 
71 #[test]
test_mount_rdonly_disallows_write()72 fn test_mount_rdonly_disallows_write() {
73     require_capability!("test_mount_rdonly_disallows_write", CAP_SYS_ADMIN);
74     let tempdir = tempfile::tempdir().unwrap();
75 
76     mount(
77         NONE,
78         tempdir.path(),
79         Some(b"tmpfs".as_ref()),
80         MsFlags::MS_RDONLY,
81         NONE,
82     )
83     .unwrap_or_else(|e| panic!("mount failed: {e}"));
84 
85     // EROFS: Read-only file system
86     assert_eq!(
87         EROFS,
88         File::create(tempdir.path().join("test"))
89             .unwrap_err()
90             .raw_os_error()
91             .unwrap()
92     );
93 
94     umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
95 }
96 
97 #[test]
test_mount_noexec_disallows_exec()98 fn test_mount_noexec_disallows_exec() {
99     require_capability!("test_mount_noexec_disallows_exec", CAP_SYS_ADMIN);
100     let tempdir = tempfile::tempdir().unwrap();
101 
102     mount(
103         NONE,
104         tempdir.path(),
105         Some(b"tmpfs".as_ref()),
106         MsFlags::MS_NOEXEC,
107         NONE,
108     )
109     .unwrap_or_else(|e| panic!("mount failed: {e}"));
110 
111     let test_path = tempdir.path().join("test");
112 
113     fs::OpenOptions::new()
114         .create(true)
115         .write(true)
116         .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
117         .open(&test_path)
118         .and_then(|mut f| f.write(SCRIPT_CONTENTS))
119         .unwrap_or_else(|e| panic!("write failed: {e}"));
120 
121     // Verify that we cannot execute despite a+x permissions being set.
122     let mode = stat::Mode::from_bits_truncate(
123         fs::metadata(&test_path)
124             .map(|md| md.permissions().mode())
125             .unwrap_or_else(|e| panic!("metadata failed: {e}")),
126     );
127 
128     assert!(
129         mode.contains(Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IXOTH),
130         "{:?} did not have execute permissions",
131         &test_path
132     );
133 
134     // while forking and unmounting prevent other child processes
135     let _m = FORK_MTX.lock();
136     // EACCES: Permission denied
137     assert_eq!(
138         EACCES,
139         Command::new(&test_path)
140             .status()
141             .unwrap_err()
142             .raw_os_error()
143             .unwrap()
144     );
145 
146     umount(tempdir.path()).unwrap_or_else(|e| panic!("umount failed: {e}"));
147 }
148 
149 #[test]
test_mount_bind()150 fn test_mount_bind() {
151     require_capability!("test_mount_bind", CAP_SYS_ADMIN);
152     let tempdir = tempfile::tempdir().unwrap();
153     let file_name = "test";
154 
155     {
156         let mount_point = tempfile::tempdir().unwrap();
157 
158         mount(
159             Some(tempdir.path()),
160             mount_point.path(),
161             NONE,
162             MsFlags::MS_BIND,
163             NONE,
164         )
165         .unwrap_or_else(|e| panic!("mount failed: {e}"));
166 
167         fs::OpenOptions::new()
168             .create(true)
169             .write(true)
170             .mode((Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO).bits())
171             .open(mount_point.path().join(file_name))
172             .and_then(|mut f| f.write(SCRIPT_CONTENTS))
173             .unwrap_or_else(|e| panic!("write failed: {e}"));
174 
175         // wait for child processes to prevent EBUSY
176         let _m = FORK_MTX.lock();
177         umount(mount_point.path())
178             .unwrap_or_else(|e| panic!("umount failed: {e}"));
179     }
180 
181     // Verify the file written in the mount shows up in source directory, even
182     // after unmounting.
183 
184     let mut buf = Vec::new();
185     File::open(tempdir.path().join(file_name))
186         .and_then(|mut f| f.read_to_end(&mut buf))
187         .unwrap_or_else(|e| panic!("read failed: {e}"));
188     assert_eq!(buf, SCRIPT_CONTENTS);
189 }
190