Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 1 | // Copyright 2021, The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | //! This module is intended for testing access control enforcement of services such as keystore2, |
| 16 | //! by assuming various identities with varying levels of privilege. Consequently, appropriate |
| 17 | //! privileges are required, or the attempt will fail causing a panic. |
| 18 | //! The `run_as` module provides the function `run_as`, which takes a UID, GID, an SELinux |
| 19 | //! context, and a closure. The return type of the closure, which is also the return type of |
| 20 | //! `run_as`, must implement `serde::Serialize` and `serde::Deserialize`. |
| 21 | //! `run_as` forks, transitions to the given identity, and executes the closure in the newly |
| 22 | //! forked process. If the closure returns, i.e., does not panic, the forked process exits with |
| 23 | //! a status of `0`, and the return value is serialized and sent through a pipe to the parent where |
| 24 | //! it gets deserialized and returned. The STDIO is not changed and the parent's panic handler |
| 25 | //! remains unchanged. So if the closure panics, the panic message is printed on the parent's STDERR |
| 26 | //! and the exit status is set to a non `0` value. The latter causes the parent to panic as well, |
| 27 | //! and if run in a test context, the test to fail. |
| 28 | |
| 29 | use keystore2_selinux as selinux; |
| 30 | use nix::sys::wait::{waitpid, WaitStatus}; |
| 31 | use nix::unistd::{ |
Frederick Mayle | 4263207 | 2024-04-08 16:51:40 -0700 | [diff] [blame] | 32 | fork, pipe as nix_pipe, read as nix_read, setgid, setuid, write as nix_write, ForkResult, Gid, |
| 33 | Pid, Uid, |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 34 | }; |
| 35 | use serde::{de::DeserializeOwned, Serialize}; |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 36 | use std::io::{Read, Write}; |
| 37 | use std::marker::PhantomData; |
Frederick Mayle | 4263207 | 2024-04-08 16:51:40 -0700 | [diff] [blame] | 38 | use std::os::fd::AsRawFd; |
| 39 | use std::os::fd::OwnedFd; |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 40 | |
| 41 | fn transition(se_context: selinux::Context, uid: Uid, gid: Gid) { |
| 42 | setgid(gid).expect("Failed to set GID. This test might need more privileges."); |
| 43 | setuid(uid).expect("Failed to set UID. This test might need more privileges."); |
| 44 | |
| 45 | selinux::setcon(&se_context) |
| 46 | .expect("Failed to set SELinux context. This test might need more privileges."); |
| 47 | } |
| 48 | |
| 49 | /// PipeReader is a simple wrapper around raw pipe file descriptors. |
| 50 | /// It takes ownership of the file descriptor and closes it on drop. It provides `read_all`, which |
| 51 | /// reads from the pipe into an expending vector, until no more data can be read. |
Frederick Mayle | 4263207 | 2024-04-08 16:51:40 -0700 | [diff] [blame] | 52 | struct PipeReader(OwnedFd); |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 53 | |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 54 | impl Read for PipeReader { |
| 55 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { |
Frederick Mayle | 4263207 | 2024-04-08 16:51:40 -0700 | [diff] [blame] | 56 | let bytes = nix_read(self.0.as_raw_fd(), buf)?; |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 57 | Ok(bytes) |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 58 | } |
| 59 | } |
| 60 | |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 61 | /// PipeWriter is a simple wrapper around raw pipe file descriptors. |
| 62 | /// It takes ownership of the file descriptor and closes it on drop. It provides `write`, which |
| 63 | /// writes the given buffer into the pipe, returning the number of bytes written. |
Frederick Mayle | 4263207 | 2024-04-08 16:51:40 -0700 | [diff] [blame] | 64 | struct PipeWriter(OwnedFd); |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 65 | |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 66 | impl Write for PipeWriter { |
| 67 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { |
Frederick Mayle | 4263207 | 2024-04-08 16:51:40 -0700 | [diff] [blame] | 68 | let written = nix_write(&self.0, buf)?; |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 69 | Ok(written) |
| 70 | } |
| 71 | |
| 72 | fn flush(&mut self) -> std::io::Result<()> { |
| 73 | // Flush is a NO-OP. |
| 74 | Ok(()) |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | /// Denotes the sender side of a serializing channel. |
| 79 | pub struct ChannelWriter<T: Serialize + DeserializeOwned>(PipeWriter, PhantomData<T>); |
| 80 | |
| 81 | impl<T: Serialize + DeserializeOwned> ChannelWriter<T> { |
| 82 | /// Sends a serializable object to a the corresponding ChannelReader. |
| 83 | /// Sending is always non blocking. Panics if any error occurs during io or serialization. |
| 84 | pub fn send(&mut self, value: &T) { |
| 85 | let serialized = serde_cbor::to_vec(value) |
| 86 | .expect("In ChannelWriter::send: Failed to serialize to vector."); |
| 87 | let size = serialized.len().to_be_bytes(); |
| 88 | match self.0.write(&size).expect("In ChannelWriter::send: Failed to write serialized size.") |
| 89 | { |
| 90 | w if w != std::mem::size_of::<usize>() => { |
| 91 | panic!( |
| 92 | "In ChannelWriter::send: Failed to write serialized size. (written: {}).", |
| 93 | w |
| 94 | ); |
| 95 | } |
| 96 | _ => {} |
| 97 | }; |
| 98 | match self |
| 99 | .0 |
| 100 | .write(&serialized) |
| 101 | .expect("In ChannelWriter::send: Failed to write serialized data.") |
| 102 | { |
| 103 | w if w != serialized.len() => { |
| 104 | panic!( |
| 105 | "In ChannelWriter::send: Failed to write serialized data. (written: {}).", |
| 106 | w |
| 107 | ); |
| 108 | } |
| 109 | _ => {} |
| 110 | }; |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | /// Represents the receiving and of a serializing channel. |
| 115 | pub struct ChannelReader<T>(PipeReader, PhantomData<T>); |
| 116 | |
| 117 | impl<T: Serialize + DeserializeOwned> ChannelReader<T> { |
| 118 | /// Receives a serializable object from the corresponding ChannelWriter. |
| 119 | /// Receiving blocks until an object of type T has been read from the channel. |
| 120 | /// Panics if an error occurs during io or deserialization. |
| 121 | pub fn recv(&mut self) -> T { |
| 122 | let mut size_buffer = [0u8; std::mem::size_of::<usize>()]; |
| 123 | match self.0.read(&mut size_buffer).expect("In ChannelReader::recv: Failed to read size.") { |
| 124 | r if r != size_buffer.len() => { |
| 125 | panic!("In ChannelReader::recv: Failed to read size. Insufficient data: {}", r); |
| 126 | } |
| 127 | _ => {} |
| 128 | }; |
| 129 | let size = usize::from_be_bytes(size_buffer); |
| 130 | let mut data_buffer = vec![0u8; size]; |
| 131 | match self |
| 132 | .0 |
| 133 | .read(&mut data_buffer) |
| 134 | .expect("In ChannelReader::recv: Failed to read serialized data.") |
| 135 | { |
| 136 | r if r != data_buffer.len() => { |
| 137 | panic!( |
| 138 | "In ChannelReader::recv: Failed to read serialized data. Insufficient data: {}", |
| 139 | r |
| 140 | ); |
| 141 | } |
| 142 | _ => {} |
| 143 | }; |
| 144 | |
| 145 | serde_cbor::from_slice(&data_buffer) |
| 146 | .expect("In ChannelReader::recv: Failed to deserialize data.") |
| 147 | } |
| 148 | } |
| 149 | |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 150 | fn pipe() -> Result<(PipeReader, PipeWriter), nix::Error> { |
| 151 | let (read_fd, write_fd) = nix_pipe()?; |
| 152 | Ok((PipeReader(read_fd), PipeWriter(write_fd))) |
| 153 | } |
| 154 | |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 155 | fn pipe_channel<T>() -> Result<(ChannelReader<T>, ChannelWriter<T>), nix::Error> |
| 156 | where |
| 157 | T: Serialize + DeserializeOwned, |
| 158 | { |
| 159 | let (reader, writer) = pipe()?; |
| 160 | Ok(( |
| 161 | ChannelReader::<T>(reader, Default::default()), |
| 162 | ChannelWriter::<T>(writer, Default::default()), |
| 163 | )) |
| 164 | } |
| 165 | |
| 166 | /// Handle for handling child processes. |
| 167 | pub struct ChildHandle<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> { |
| 168 | pid: Pid, |
| 169 | result_reader: ChannelReader<R>, |
| 170 | cmd_writer: ChannelWriter<M>, |
| 171 | response_reader: ChannelReader<M>, |
| 172 | exit_status: Option<WaitStatus>, |
| 173 | } |
| 174 | |
| 175 | impl<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> ChildHandle<R, M> { |
| 176 | /// Send a command message to the child. |
| 177 | pub fn send(&mut self, data: &M) { |
| 178 | self.cmd_writer.send(data) |
| 179 | } |
| 180 | |
| 181 | /// Receive a response from the child. |
| 182 | pub fn recv(&mut self) -> M { |
| 183 | self.response_reader.recv() |
| 184 | } |
| 185 | |
| 186 | /// Get child result. Panics if the child did not exit with status 0 or if a serialization |
| 187 | /// error occurred. |
| 188 | pub fn get_result(mut self) -> R { |
| 189 | let status = |
| 190 | waitpid(self.pid, None).expect("ChildHandle::wait: Failed while waiting for child."); |
| 191 | match status { |
| 192 | WaitStatus::Exited(pid, 0) => { |
| 193 | // Child exited successfully. |
| 194 | // Read the result from the pipe. |
| 195 | self.exit_status = Some(WaitStatus::Exited(pid, 0)); |
| 196 | self.result_reader.recv() |
| 197 | } |
| 198 | WaitStatus::Exited(pid, c) => { |
| 199 | panic!("Child did not exit as expected: {:?}", WaitStatus::Exited(pid, c)); |
| 200 | } |
| 201 | status => { |
| 202 | panic!("Child did not exit at all: {:?}", status); |
| 203 | } |
| 204 | } |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | impl<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> Drop for ChildHandle<R, M> { |
| 209 | fn drop(&mut self) { |
| 210 | if self.exit_status.is_none() { |
| 211 | panic!("Child result not checked.") |
| 212 | } |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | /// Run the given closure in a new process running with the new identity given as |
| 217 | /// `uid`, `gid`, and `se_context`. Parent process will run without waiting for child status. |
| 218 | /// |
| 219 | /// # Safety |
| 220 | /// run_as_child runs the given closure in the client branch of fork. And it uses non |
| 221 | /// async signal safe API. This means that calling this function in a multi threaded program |
| 222 | /// yields undefined behavior in the child. As of this writing, it is safe to call this function |
| 223 | /// from a Rust device test, because every test itself is spawned as a separate process. |
| 224 | /// |
| 225 | /// # Safety Binder |
| 226 | /// It is okay for the closure to use binder services, however, this does not work |
| 227 | /// if the parent initialized libbinder already. So do not use binder outside of the closure |
| 228 | /// in your test. |
| 229 | pub unsafe fn run_as_child<F, R, M>( |
| 230 | se_context: &str, |
| 231 | uid: Uid, |
| 232 | gid: Gid, |
| 233 | f: F, |
| 234 | ) -> Result<ChildHandle<R, M>, nix::Error> |
| 235 | where |
| 236 | R: Serialize + DeserializeOwned, |
| 237 | M: Serialize + DeserializeOwned, |
| 238 | F: 'static + Send + FnOnce(&mut ChannelReader<M>, &mut ChannelWriter<M>) -> R, |
| 239 | { |
| 240 | let se_context = |
| 241 | selinux::Context::new(se_context).expect("Unable to construct selinux::Context."); |
| 242 | let (result_reader, mut result_writer) = pipe_channel().expect("Failed to create pipe."); |
| 243 | let (mut cmd_reader, cmd_writer) = pipe_channel().expect("Failed to create cmd pipe."); |
| 244 | let (response_reader, mut response_writer) = |
| 245 | pipe_channel().expect("Failed to create cmd pipe."); |
| 246 | |
Andrew Walbran | a47698a | 2023-07-21 17:23:56 +0100 | [diff] [blame] | 247 | // SAFETY: Our caller guarantees that the process only has a single thread, so calling |
| 248 | // non-async-signal-safe functions in the child is in fact safe. |
| 249 | match unsafe { fork() } { |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 250 | Ok(ForkResult::Parent { child, .. }) => { |
| 251 | drop(response_writer); |
| 252 | drop(cmd_reader); |
| 253 | drop(result_writer); |
| 254 | |
| 255 | Ok(ChildHandle::<R, M> { |
| 256 | pid: child, |
| 257 | result_reader, |
| 258 | response_reader, |
| 259 | cmd_writer, |
| 260 | exit_status: None, |
| 261 | }) |
| 262 | } |
| 263 | Ok(ForkResult::Child) => { |
| 264 | drop(cmd_writer); |
| 265 | drop(response_reader); |
| 266 | drop(result_reader); |
| 267 | |
| 268 | // This will panic on error or insufficient privileges. |
| 269 | transition(se_context, uid, gid); |
| 270 | |
| 271 | // Run the closure. |
| 272 | let result = f(&mut cmd_reader, &mut response_writer); |
| 273 | |
| 274 | // Serialize the result of the closure. |
| 275 | result_writer.send(&result); |
| 276 | |
| 277 | // Set exit status to `0`. |
| 278 | std::process::exit(0); |
| 279 | } |
| 280 | Err(errno) => { |
| 281 | panic!("Failed to fork: {:?}", errno); |
| 282 | } |
| 283 | } |
| 284 | } |
| 285 | |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 286 | /// Run the given closure in a new process running with the new identity given as |
| 287 | /// `uid`, `gid`, and `se_context`. |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 288 | /// |
| 289 | /// # Safety |
| 290 | /// run_as runs the given closure in the client branch of fork. And it uses non |
| 291 | /// async signal safe API. This means that calling this function in a multi threaded program |
| 292 | /// yields undefined behavior in the child. As of this writing, it is safe to call this function |
| 293 | /// from a Rust device test, because every test itself is spawned as a separate process. |
| 294 | /// |
| 295 | /// # Safety Binder |
| 296 | /// It is okay for the closure to use binder services, however, this does not work |
| 297 | /// if the parent initialized libbinder already. So do not use binder outside of the closure |
| 298 | /// in your test. |
| 299 | pub unsafe fn run_as<F, R>(se_context: &str, uid: Uid, gid: Gid, f: F) -> R |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 300 | where |
| 301 | R: Serialize + DeserializeOwned, |
| 302 | F: 'static + Send + FnOnce() -> R, |
| 303 | { |
| 304 | let se_context = |
| 305 | selinux::Context::new(se_context).expect("Unable to construct selinux::Context."); |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 306 | let (mut reader, mut writer) = pipe_channel::<R>().expect("Failed to create pipe."); |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 307 | |
Andrew Walbran | a47698a | 2023-07-21 17:23:56 +0100 | [diff] [blame] | 308 | // SAFETY: Our caller guarantees that the process only has a single thread, so calling |
| 309 | // non-async-signal-safe functions in the child is in fact safe. |
| 310 | match unsafe { fork() } { |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 311 | Ok(ForkResult::Parent { child, .. }) => { |
| 312 | drop(writer); |
| 313 | let status = waitpid(child, None).expect("Failed while waiting for child."); |
| 314 | if let WaitStatus::Exited(_, 0) = status { |
| 315 | // Child exited successfully. |
| 316 | // Read the result from the pipe. |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 317 | // let serialized_result = |
| 318 | // reader.read_all().expect("Failed to read result from child."); |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 319 | |
| 320 | // Deserialize the result and return it. |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 321 | reader.recv() |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 322 | } else { |
| 323 | panic!("Child did not exit as expected {:?}", status); |
| 324 | } |
| 325 | } |
| 326 | Ok(ForkResult::Child) => { |
| 327 | // This will panic on error or insufficient privileges. |
| 328 | transition(se_context, uid, gid); |
| 329 | |
| 330 | // Run the closure. |
| 331 | let result = f(); |
| 332 | |
| 333 | // Serialize the result of the closure. |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 334 | writer.send(&result); |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 335 | |
| 336 | // Set exit status to `0`. |
| 337 | std::process::exit(0); |
| 338 | } |
| 339 | Err(errno) => { |
| 340 | panic!("Failed to fork: {:?}", errno); |
| 341 | } |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | #[cfg(test)] |
| 346 | mod test { |
| 347 | use super::*; |
| 348 | use keystore2_selinux as selinux; |
| 349 | use nix::unistd::{getgid, getuid}; |
| 350 | use serde::{Deserialize, Serialize}; |
| 351 | |
| 352 | /// This test checks that the closure does not produce an exit status of `0` when run inside a |
| 353 | /// test and the closure panics. This would mask test failures as success. |
| 354 | #[test] |
| 355 | #[should_panic] |
| 356 | fn test_run_as_panics_on_closure_panic() { |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 357 | // Safety: run_as must be called from a single threaded process. |
| 358 | // This device test is run as a separate single threaded process. |
| 359 | unsafe { |
Chris Wailes | 00fa9bd | 2024-09-05 13:33:08 -0700 | [diff] [blame] | 360 | run_as::<_, ()>( |
| 361 | selinux::getcon().unwrap().to_str().unwrap(), |
| 362 | getuid(), |
| 363 | getgid(), |
| 364 | || panic!("Closure must panic."), |
| 365 | ) |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 366 | }; |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 367 | } |
| 368 | |
| 369 | static TARGET_UID: Uid = Uid::from_raw(10020); |
| 370 | static TARGET_GID: Gid = Gid::from_raw(10020); |
| 371 | static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20"; |
| 372 | |
| 373 | /// Tests that the closure is running as the target identity. |
| 374 | #[test] |
| 375 | fn test_transition_to_untrusted_app() { |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 376 | // Safety: run_as must be called from a single threaded process. |
| 377 | // This device test is run as a separate single threaded process. |
| 378 | unsafe { |
| 379 | run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || { |
| 380 | assert_eq!(TARGET_UID, getuid()); |
| 381 | assert_eq!(TARGET_GID, getgid()); |
| 382 | assert_eq!(TARGET_CTX, selinux::getcon().unwrap().to_str().unwrap()); |
| 383 | }) |
| 384 | }; |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 385 | } |
| 386 | |
| 387 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] |
| 388 | struct SomeResult { |
| 389 | a: u32, |
| 390 | b: u64, |
| 391 | c: String, |
| 392 | } |
| 393 | |
| 394 | #[test] |
| 395 | fn test_serialized_result() { |
| 396 | let test_result = SomeResult { |
| 397 | a: 5, |
| 398 | b: 0xffffffffffffffff, |
| 399 | c: "supercalifragilisticexpialidocious".to_owned(), |
| 400 | }; |
| 401 | let test_result_clone = test_result.clone(); |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 402 | // Safety: run_as must be called from a single threaded process. |
| 403 | // This device test is run as a separate single threaded process. |
| 404 | let result = unsafe { run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || test_result_clone) }; |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 405 | assert_eq!(test_result, result); |
| 406 | } |
Janis Danisevskis | 04945eb | 2021-12-06 17:20:09 -0800 | [diff] [blame] | 407 | |
| 408 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] |
| 409 | enum PingPong { |
| 410 | Ping, |
| 411 | Pong, |
| 412 | } |
| 413 | |
| 414 | /// Tests that closure is running under given user identity and communicates with calling |
| 415 | /// process using pipe. |
| 416 | #[test] |
| 417 | fn test_run_as_child() { |
| 418 | let test_result = SomeResult { |
| 419 | a: 5, |
| 420 | b: 0xffffffffffffffff, |
| 421 | c: "supercalifragilisticexpialidocious".to_owned(), |
| 422 | }; |
| 423 | let test_result_clone = test_result.clone(); |
| 424 | |
| 425 | // Safety: run_as_child must be called from a single threaded process. |
| 426 | // This device test is run as a separate single threaded process. |
| 427 | let mut child_handle: ChildHandle<SomeResult, PingPong> = unsafe { |
| 428 | run_as_child(TARGET_CTX, TARGET_UID, TARGET_GID, |cmd_reader, response_writer| { |
| 429 | assert_eq!(TARGET_UID, getuid()); |
| 430 | assert_eq!(TARGET_GID, getgid()); |
| 431 | assert_eq!(TARGET_CTX, selinux::getcon().unwrap().to_str().unwrap()); |
| 432 | |
| 433 | let ping: PingPong = cmd_reader.recv(); |
| 434 | assert_eq!(ping, PingPong::Ping); |
| 435 | |
| 436 | response_writer.send(&PingPong::Pong); |
| 437 | |
| 438 | let ping: PingPong = cmd_reader.recv(); |
| 439 | assert_eq!(ping, PingPong::Ping); |
| 440 | let pong: PingPong = cmd_reader.recv(); |
| 441 | assert_eq!(pong, PingPong::Pong); |
| 442 | |
| 443 | response_writer.send(&PingPong::Pong); |
| 444 | response_writer.send(&PingPong::Ping); |
| 445 | |
| 446 | test_result_clone |
| 447 | }) |
| 448 | .unwrap() |
| 449 | }; |
| 450 | |
| 451 | // Send one ping. |
| 452 | child_handle.send(&PingPong::Ping); |
| 453 | |
| 454 | // Expect one pong. |
| 455 | let pong = child_handle.recv(); |
| 456 | assert_eq!(pong, PingPong::Pong); |
| 457 | |
| 458 | // Send ping and pong. |
| 459 | child_handle.send(&PingPong::Ping); |
| 460 | child_handle.send(&PingPong::Pong); |
| 461 | |
| 462 | // Expect pong and ping. |
| 463 | let pong = child_handle.recv(); |
| 464 | assert_eq!(pong, PingPong::Pong); |
| 465 | let ping = child_handle.recv(); |
| 466 | assert_eq!(ping, PingPong::Ping); |
| 467 | |
| 468 | assert_eq!(child_handle.get_result(), test_result); |
| 469 | } |
Janis Danisevskis | a578d39 | 2021-09-20 15:44:06 -0700 | [diff] [blame] | 470 | } |