xref: /aosp_15_r20/system/security/keystore2/test_utils/run_as.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
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::{
32     fork, pipe as nix_pipe, read as nix_read, setgid, setuid, write as nix_write, ForkResult, Gid,
33     Pid, Uid,
34 };
35 use serde::{de::DeserializeOwned, Deserialize, Serialize};
36 use std::io::{Read, Write};
37 use std::marker::PhantomData;
38 use std::os::fd::AsRawFd;
39 use std::os::fd::OwnedFd;
40 
41 /// Newtype string error, which can be serialized and transferred out from a sub-process.
42 #[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
43 pub struct Error(pub String);
44 
45 /// Allow ergonomic use of [`anyhow::Error`].
46 impl From<anyhow::Error> for Error {
from(err: anyhow::Error) -> Self47     fn from(err: anyhow::Error) -> Self {
48         // Use the debug format of [`anyhow::Error`] to include backtrace.
49         Self(format!("{:?}", err))
50     }
51 }
52 impl From<String> for Error {
from(val: String) -> Self53     fn from(val: String) -> Self {
54         Self(val)
55     }
56 }
57 impl From<&str> for Error {
from(val: &str) -> Self58     fn from(val: &str) -> Self {
59         Self(val.to_string())
60     }
61 }
62 
63 impl std::fmt::Display for Error {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result64     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65         write!(f, "{}", self.0)
66     }
67 }
68 
69 impl std::error::Error for Error {}
70 
71 /// Equivalent to the [`assert!`] macro which returns an [`Error`] rather than emitting a panic.
72 /// This is useful for test code that is `run_as`, so failures are more accessible.
73 #[macro_export]
74 macro_rules! expect {
75     ($cond:expr $(,)?) => {{
76         let result = $cond;
77         if !result {
78             return Err($crate::run_as::Error(format!(
79                 "{}:{}: check '{}' failed",
80                 file!(),
81                 line!(),
82                 stringify!($cond)
83             )));
84         }
85     }};
86     ($cond:expr, $($arg:tt)+) => {{
87         let result = $cond;
88         if !result {
89             return Err($crate::run_as::Error(format!(
90                 "{}:{}: check '{}' failed: {}",
91                 file!(),
92                 line!(),
93                 stringify!($cond),
94                 format_args!($($arg)+)
95             )));
96         }
97     }};
98 }
99 
100 /// Equivalent to the [`assert_eq!`] macro which returns an [`Error`] rather than emitting a panic.
101 /// This is useful for test code that is `run_as`, so failures are more accessible.
102 #[macro_export]
103 macro_rules! expect_eq {
104     ($left:expr, $right:expr $(,)?) => {{
105         let left = $left;
106         let right = $right;
107         if left != right {
108             return Err($crate::run_as::Error(format!(
109                 "{}:{}: assertion {} == {} failed\n  left: {left:?}\n right: {right:?}\n",
110                 file!(),
111                 line!(),
112                 stringify!($left),
113                 stringify!($right),
114             )));
115         }
116     }};
117     ($left:expr, $right:expr, $($arg:tt)+) => {{
118         let left = $left;
119         let right = $right;
120         if left != right {
121             return Err($crate::run_as::Error(format!(
122                 "{}:{}: assertion {} == {} failed: {}\n  left: {left:?}\n right: {right:?}\n",
123                 file!(),
124                 line!(),
125                 stringify!($left),
126                 stringify!($right),
127                 format_args!($($arg)+)
128             )));
129         }
130     }};
131 }
132 
transition(se_context: selinux::Context, uid: Uid, gid: Gid)133 fn transition(se_context: selinux::Context, uid: Uid, gid: Gid) {
134     setgid(gid).expect("Failed to set GID. This test might need more privileges.");
135     setuid(uid).expect("Failed to set UID. This test might need more privileges.");
136 
137     selinux::setcon(&se_context)
138         .expect("Failed to set SELinux context. This test might need more privileges.");
139 }
140 
141 /// PipeReader is a simple wrapper around raw pipe file descriptors.
142 /// It takes ownership of the file descriptor and closes it on drop. It provides `read_all`, which
143 /// reads from the pipe into an expending vector, until no more data can be read.
144 struct PipeReader(OwnedFd);
145 
146 impl Read for PipeReader {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>147     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
148         let bytes = nix_read(self.0.as_raw_fd(), buf)?;
149         Ok(bytes)
150     }
151 }
152 
153 /// PipeWriter is a simple wrapper around raw pipe file descriptors.
154 /// It takes ownership of the file descriptor and closes it on drop. It provides `write`, which
155 /// writes the given buffer into the pipe, returning the number of bytes written.
156 struct PipeWriter(OwnedFd);
157 
158 impl Write for PipeWriter {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>159     fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
160         let written = nix_write(&self.0, buf)?;
161         Ok(written)
162     }
163 
flush(&mut self) -> std::io::Result<()>164     fn flush(&mut self) -> std::io::Result<()> {
165         // Flush is a NO-OP.
166         Ok(())
167     }
168 }
169 
170 /// Denotes the sender side of a serializing channel.
171 pub struct ChannelWriter<T: Serialize + DeserializeOwned>(PipeWriter, PhantomData<T>);
172 
173 impl<T: Serialize + DeserializeOwned> ChannelWriter<T> {
174     /// Sends a serializable object to a the corresponding ChannelReader.
175     /// Sending is always non blocking. Panics if any error occurs during io or serialization.
send(&mut self, value: &T)176     pub fn send(&mut self, value: &T) {
177         let serialized = serde_cbor::to_vec(value)
178             .expect("In ChannelWriter::send: Failed to serialize to vector.");
179         let size = serialized.len().to_be_bytes();
180         match self.0.write(&size).expect("In ChannelWriter::send: Failed to write serialized size.")
181         {
182             w if w != std::mem::size_of::<usize>() => {
183                 panic!(
184                     "In ChannelWriter::send: Failed to write serialized size. (written: {}).",
185                     w
186                 );
187             }
188             _ => {}
189         };
190         match self
191             .0
192             .write(&serialized)
193             .expect("In ChannelWriter::send: Failed to write serialized data.")
194         {
195             w if w != serialized.len() => {
196                 panic!(
197                     "In ChannelWriter::send: Failed to write serialized data. (written: {}).",
198                     w
199                 );
200             }
201             _ => {}
202         };
203     }
204 }
205 
206 /// Represents the receiving and of a serializing channel.
207 pub struct ChannelReader<T>(PipeReader, PhantomData<T>);
208 
209 impl<T: Serialize + DeserializeOwned> ChannelReader<T> {
210     /// Receives a serializable object from the corresponding ChannelWriter.
211     /// Receiving blocks until an object of type T has been read from the channel.
212     /// Panics if an error occurs during io or deserialization.
recv(&mut self) -> T213     pub fn recv(&mut self) -> T {
214         match self.recv_err() {
215             Ok(val) => val,
216             Err(e) => panic!("{e}"),
217         }
218     }
219 
220     /// Receives a serializable object from the corresponding ChannelWriter.
221     /// Receiving blocks until an object of type T has been read from the channel.
recv_err(&mut self) -> Result<T, Error>222     pub fn recv_err(&mut self) -> Result<T, Error> {
223         let mut size_buffer = [0u8; std::mem::size_of::<usize>()];
224         match self.0.read(&mut size_buffer).expect("In ChannelReader::recv: Failed to read size.") {
225             r if r != size_buffer.len() => {
226                 return Err(format!(
227                     "In ChannelReader::recv: Failed to read size. Insufficient data: {}",
228                     r
229                 )
230                 .into());
231             }
232             _ => {}
233         };
234         let size = usize::from_be_bytes(size_buffer);
235         let mut data_buffer = vec![0u8; size];
236         match self.0.read(&mut data_buffer) {
237             Ok(r) if r != data_buffer.len() => {
238                 return Err(format!(
239                     "In ChannelReader::recv: Failed to read serialized data. Insufficient data: {}",
240                     r
241                 )
242                 .into());
243             }
244             Ok(_) => {}
245             Err(e) => {
246                 return Err(format!(
247                     "In ChannelReader::recv: Failed to read serialized data: {e:?}"
248                 )
249                 .into())
250             }
251         };
252 
253         serde_cbor::from_slice(&data_buffer).map_err(|e| {
254             format!("In ChannelReader::recv: Failed to deserialize data: {e:?}").into()
255         })
256     }
257 }
258 
pipe() -> Result<(PipeReader, PipeWriter), nix::Error>259 fn pipe() -> Result<(PipeReader, PipeWriter), nix::Error> {
260     let (read_fd, write_fd) = nix_pipe()?;
261     Ok((PipeReader(read_fd), PipeWriter(write_fd)))
262 }
263 
pipe_channel<T>() -> Result<(ChannelReader<T>, ChannelWriter<T>), nix::Error> where T: Serialize + DeserializeOwned,264 fn pipe_channel<T>() -> Result<(ChannelReader<T>, ChannelWriter<T>), nix::Error>
265 where
266     T: Serialize + DeserializeOwned,
267 {
268     let (reader, writer) = pipe()?;
269     Ok((
270         ChannelReader::<T>(reader, Default::default()),
271         ChannelWriter::<T>(writer, Default::default()),
272     ))
273 }
274 
275 /// Handle for handling child processes.
276 pub struct ChildHandle<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> {
277     pid: Pid,
278     result_reader: ChannelReader<R>,
279     cmd_writer: ChannelWriter<M>,
280     response_reader: ChannelReader<M>,
281     exit_status: Option<WaitStatus>,
282 }
283 
284 impl<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> ChildHandle<R, M> {
285     /// Send a command message to the child.
send(&mut self, data: &M)286     pub fn send(&mut self, data: &M) {
287         self.cmd_writer.send(data)
288     }
289 
290     /// Receive a response from the child.
recv(&mut self) -> M291     pub fn recv(&mut self) -> M {
292         self.response_reader.recv()
293     }
294 
295     /// Get child result. Panics if the child did not exit with status 0 or if a serialization
296     /// error occurred.
get_result(mut self) -> R297     pub fn get_result(mut self) -> R {
298         self.get_death_result()
299     }
300 
301     /// Get child result via a mutable reference.
get_death_result(&mut self) -> R302     fn get_death_result(&mut self) -> R {
303         let status =
304             waitpid(self.pid, None).expect("ChildHandle::wait: Failed while waiting for child.");
305         match status {
306             WaitStatus::Exited(pid, 0) => {
307                 // Child exited successfully.
308                 // Read the result from the pipe.
309                 self.exit_status = Some(WaitStatus::Exited(pid, 0));
310                 self.result_reader.recv()
311             }
312             WaitStatus::Exited(pid, c) => {
313                 panic!("Child did not exit as expected: {:?}", WaitStatus::Exited(pid, c));
314             }
315             status => {
316                 panic!("Child did not exit at all: {:?}", status);
317             }
318         }
319     }
320 }
321 
322 impl<R, M> ChildHandle<Result<R, Error>, M>
323 where
324     R: Serialize + DeserializeOwned,
325     M: Serialize + DeserializeOwned,
326 {
327     /// Receive a response from the child.  If the child has closed the response
328     /// channel, assume it has terminated and read the final result.
329     /// Panics on child failure, but will display the child error value.
recv_or_die(&mut self) -> M330     pub fn recv_or_die(&mut self) -> M {
331         match self.response_reader.recv_err() {
332             Ok(v) => v,
333             Err(_e) => {
334                 // We have failed to read from the `response_reader` channel.
335                 // Assume this is because the child completed early with an error.
336                 match self.get_death_result() {
337                     Ok(_) => {
338                         panic!("Child completed OK despite failure to read a response!")
339                     }
340                     Err(e) => panic!("Child failed with:\n{e}"),
341                 }
342             }
343         }
344     }
345 }
346 
347 impl<R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned> Drop for ChildHandle<R, M> {
drop(&mut self)348     fn drop(&mut self) {
349         if self.exit_status.is_none() {
350             panic!("Child result not checked.")
351         }
352     }
353 }
354 
355 /// Run the given closure in a new process running as an untrusted app with the given `uid` and
356 /// `gid`. Parent process will run without waiting for child status.
357 ///
358 /// # Safety
359 /// run_as_child runs the given closure in the client branch of fork. And it uses non
360 /// async signal safe API. This means that calling this function in a multi threaded program
361 /// yields undefined behavior in the child. As of this writing, it is safe to call this function
362 /// from a Rust device test, because every test itself is spawned as a separate process.
363 ///
364 /// # Safety Binder
365 /// It is okay for the closure to use binder services, however, this does not work
366 /// if the parent initialized libbinder already. So do not use binder outside of the closure
367 /// in your test.
run_as_child_app<F, R, M>( uid: u32, gid: u32, f: F, ) -> Result<ChildHandle<R, M>, nix::Error> where R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned, F: 'static + Send + FnOnce(&mut ChannelReader<M>, &mut ChannelWriter<M>) -> R,368 pub unsafe fn run_as_child_app<F, R, M>(
369     uid: u32,
370     gid: u32,
371     f: F,
372 ) -> Result<ChildHandle<R, M>, nix::Error>
373 where
374     R: Serialize + DeserializeOwned,
375     M: Serialize + DeserializeOwned,
376     F: 'static + Send + FnOnce(&mut ChannelReader<M>, &mut ChannelWriter<M>) -> R,
377 {
378     // Safety: Caller guarantees that the process only has a single thread.
379     unsafe {
380         run_as_child(
381             "u:r:untrusted_app:s0:c91,c256,c10,c20",
382             Uid::from_raw(uid),
383             Gid::from_raw(gid),
384             f,
385         )
386     }
387 }
388 
389 /// Run the given closure in a new process running with the new identity given as
390 /// `uid`, `gid`, and `se_context`. Parent process will run without waiting for child status.
391 ///
392 /// # Safety
393 /// run_as_child runs the given closure in the client branch of fork. And it uses non
394 /// async signal safe API. This means that calling this function in a multi threaded program
395 /// yields undefined behavior in the child. As of this writing, it is safe to call this function
396 /// from a Rust device test, because every test itself is spawned as a separate process.
397 ///
398 /// # Safety Binder
399 /// It is okay for the closure to use binder services, however, this does not work
400 /// if the parent initialized libbinder already. So do not use binder outside of the closure
401 /// in your test.
run_as_child<F, R, M>( se_context: &str, uid: Uid, gid: Gid, f: F, ) -> Result<ChildHandle<R, M>, nix::Error> where R: Serialize + DeserializeOwned, M: Serialize + DeserializeOwned, F: 'static + Send + FnOnce(&mut ChannelReader<M>, &mut ChannelWriter<M>) -> R,402 pub unsafe fn run_as_child<F, R, M>(
403     se_context: &str,
404     uid: Uid,
405     gid: Gid,
406     f: F,
407 ) -> Result<ChildHandle<R, M>, nix::Error>
408 where
409     R: Serialize + DeserializeOwned,
410     M: Serialize + DeserializeOwned,
411     F: 'static + Send + FnOnce(&mut ChannelReader<M>, &mut ChannelWriter<M>) -> R,
412 {
413     let se_context =
414         selinux::Context::new(se_context).expect("Unable to construct selinux::Context.");
415     let (result_reader, mut result_writer) = pipe_channel().expect("Failed to create pipe.");
416     let (mut cmd_reader, cmd_writer) = pipe_channel().expect("Failed to create cmd pipe.");
417     let (response_reader, mut response_writer) =
418         pipe_channel().expect("Failed to create cmd pipe.");
419 
420     // Safety: Our caller guarantees that the process only has a single thread, so calling
421     // non-async-signal-safe functions in the child is in fact safe.
422     match unsafe { fork() } {
423         Ok(ForkResult::Parent { child, .. }) => {
424             drop(response_writer);
425             drop(cmd_reader);
426             drop(result_writer);
427 
428             Ok(ChildHandle::<R, M> {
429                 pid: child,
430                 result_reader,
431                 response_reader,
432                 cmd_writer,
433                 exit_status: None,
434             })
435         }
436         Ok(ForkResult::Child) => {
437             drop(cmd_writer);
438             drop(response_reader);
439             drop(result_reader);
440 
441             // This will panic on error or insufficient privileges.
442             transition(se_context, uid, gid);
443 
444             // Run the closure.
445             let result = f(&mut cmd_reader, &mut response_writer);
446 
447             // Serialize the result of the closure.
448             result_writer.send(&result);
449 
450             // Set exit status to `0`.
451             std::process::exit(0);
452         }
453         Err(errno) => {
454             panic!("Failed to fork: {:?}", errno);
455         }
456     }
457 }
458 
459 /// Run the given closure in a new process running with the root identity.
460 ///
461 /// # Safety
462 /// run_as runs the given closure in the client branch of fork. And it uses non
463 /// async signal safe API. This means that calling this function in a multi threaded program
464 /// yields undefined behavior in the child. As of this writing, it is safe to call this function
465 /// from a Rust device test, because every test itself is spawned as a separate process.
466 ///
467 /// # Safety Binder
468 /// It is okay for the closure to use binder services, however, this does not work
469 /// if the parent initialized libbinder already. So do not use binder outside of the closure
470 /// in your test.
run_as_root<F, R>(f: F) -> R where R: Serialize + DeserializeOwned, F: 'static + Send + FnOnce() -> R,471 pub unsafe fn run_as_root<F, R>(f: F) -> R
472 where
473     R: Serialize + DeserializeOwned,
474     F: 'static + Send + FnOnce() -> R,
475 {
476     // SAFETY: Our caller guarantees that the process only has a single thread.
477     unsafe { run_as("u:r:su:s0", Uid::from_raw(0), Gid::from_raw(0), f) }
478 }
479 
480 /// Run the given closure in a new `untrusted_app` process running with the given `uid` and `gid`.
481 ///
482 /// # Safety
483 /// run_as runs the given closure in the client branch of fork. And it uses non
484 /// async signal safe API. This means that calling this function in a multi threaded program
485 /// yields undefined behavior in the child. As of this writing, it is safe to call this function
486 /// from a Rust device test, because every test itself is spawned as a separate process.
487 ///
488 /// # Safety Binder
489 /// It is okay for the closure to use binder services, however, this does not work
490 /// if the parent initialized libbinder already. So do not use binder outside of the closure
491 /// in your test.
run_as_app<F, R>(uid: u32, gid: u32, f: F) -> R where R: Serialize + DeserializeOwned, F: 'static + Send + FnOnce() -> R,492 pub unsafe fn run_as_app<F, R>(uid: u32, gid: u32, f: F) -> R
493 where
494     R: Serialize + DeserializeOwned,
495     F: 'static + Send + FnOnce() -> R,
496 {
497     // SAFETY: Our caller guarantees that the process only has a single thread.
498     unsafe {
499         run_as("u:r:untrusted_app:s0:c91,c256,c10,c20", Uid::from_raw(uid), Gid::from_raw(gid), f)
500     }
501 }
502 
503 /// Run the given closure in a new process running with the new identity given as
504 /// `uid`, `gid`, and `se_context`.
505 ///
506 /// # Safety
507 /// run_as runs the given closure in the client branch of fork. And it uses non
508 /// async signal safe API. This means that calling this function in a multi threaded program
509 /// yields undefined behavior in the child. As of this writing, it is safe to call this function
510 /// from a Rust device test, because every test itself is spawned as a separate process.
511 ///
512 /// # Safety Binder
513 /// It is okay for the closure to use binder services, however, this does not work
514 /// if the parent initialized libbinder already. So do not use binder outside of the closure
515 /// in your test.
run_as<F, R>(se_context: &str, uid: Uid, gid: Gid, f: F) -> R where R: Serialize + DeserializeOwned, F: 'static + Send + FnOnce() -> R,516 pub unsafe fn run_as<F, R>(se_context: &str, uid: Uid, gid: Gid, f: F) -> R
517 where
518     R: Serialize + DeserializeOwned,
519     F: 'static + Send + FnOnce() -> R,
520 {
521     let se_context =
522         selinux::Context::new(se_context).expect("Unable to construct selinux::Context.");
523     let (mut reader, mut writer) = pipe_channel::<R>().expect("Failed to create pipe.");
524 
525     // SAFETY: Our caller guarantees that the process only has a single thread, so calling
526     // non-async-signal-safe functions in the child is in fact safe.
527     match unsafe { fork() } {
528         Ok(ForkResult::Parent { child, .. }) => {
529             drop(writer);
530             let status = waitpid(child, None).expect("Failed while waiting for child.");
531             if let WaitStatus::Exited(_, 0) = status {
532                 // Child exited successfully.
533                 // Read the result from the pipe.
534                 // let serialized_result =
535                 //     reader.read_all().expect("Failed to read result from child.");
536 
537                 // Deserialize the result and return it.
538                 reader.recv()
539             } else {
540                 panic!("Child did not exit as expected {:?}", status);
541             }
542         }
543         Ok(ForkResult::Child) => {
544             // This will panic on error or insufficient privileges.
545             transition(se_context, uid, gid);
546 
547             // Run the closure.
548             let result = f();
549 
550             // Serialize the result of the closure.
551             writer.send(&result);
552 
553             // Set exit status to `0`.
554             std::process::exit(0);
555         }
556         Err(errno) => {
557             panic!("Failed to fork: {:?}", errno);
558         }
559     }
560 }
561 
562 #[cfg(test)]
563 mod test {
564     use super::*;
565     use keystore2_selinux as selinux;
566     use nix::unistd::{getgid, getuid};
567     use serde::{Deserialize, Serialize};
568 
569     /// This test checks that the closure does not produce an exit status of `0` when run inside a
570     /// test and the closure panics. This would mask test failures as success.
571     #[test]
572     #[should_panic]
test_run_as_panics_on_closure_panic()573     fn test_run_as_panics_on_closure_panic() {
574         // Safety: run_as must be called from a single threaded process.
575         // This device test is run as a separate single threaded process.
576         unsafe {
577             run_as::<_, ()>(
578                 selinux::getcon().unwrap().to_str().unwrap(),
579                 getuid(),
580                 getgid(),
581                 || panic!("Closure must panic."),
582             )
583         };
584     }
585 
586     static TARGET_UID: Uid = Uid::from_raw(10020);
587     static TARGET_GID: Gid = Gid::from_raw(10020);
588     static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
589 
590     /// Tests that the closure is running as the target identity.
591     #[test]
test_transition_to_untrusted_app()592     fn test_transition_to_untrusted_app() {
593         // Safety: run_as must be called from a single threaded process.
594         // This device test is run as a separate single threaded process.
595         unsafe {
596             run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || {
597                 assert_eq!(TARGET_UID, getuid());
598                 assert_eq!(TARGET_GID, getgid());
599                 assert_eq!(TARGET_CTX, selinux::getcon().unwrap().to_str().unwrap());
600             })
601         };
602     }
603 
604     #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
605     struct SomeResult {
606         a: u32,
607         b: u64,
608         c: String,
609     }
610 
611     #[test]
test_serialized_result()612     fn test_serialized_result() {
613         let test_result = SomeResult {
614             a: 5,
615             b: 0xffffffffffffffff,
616             c: "supercalifragilisticexpialidocious".to_owned(),
617         };
618         let test_result_clone = test_result.clone();
619         // Safety: run_as must be called from a single threaded process.
620         // This device test is run as a separate single threaded process.
621         let result = unsafe { run_as(TARGET_CTX, TARGET_UID, TARGET_GID, || test_result_clone) };
622         assert_eq!(test_result, result);
623     }
624 
625     #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
626     enum PingPong {
627         Ping,
628         Pong,
629     }
630 
631     /// Tests that closure is running under given user identity and communicates with calling
632     /// process using pipe.
633     #[test]
test_run_as_child()634     fn test_run_as_child() {
635         let test_result = SomeResult {
636             a: 5,
637             b: 0xffffffffffffffff,
638             c: "supercalifragilisticexpialidocious".to_owned(),
639         };
640         let test_result_clone = test_result.clone();
641 
642         // Safety: run_as_child must be called from a single threaded process.
643         // This device test is run as a separate single threaded process.
644         let mut child_handle: ChildHandle<SomeResult, PingPong> = unsafe {
645             run_as_child(TARGET_CTX, TARGET_UID, TARGET_GID, |cmd_reader, response_writer| {
646                 assert_eq!(TARGET_UID, getuid());
647                 assert_eq!(TARGET_GID, getgid());
648                 assert_eq!(TARGET_CTX, selinux::getcon().unwrap().to_str().unwrap());
649 
650                 let ping: PingPong = cmd_reader.recv();
651                 assert_eq!(ping, PingPong::Ping);
652 
653                 response_writer.send(&PingPong::Pong);
654 
655                 let ping: PingPong = cmd_reader.recv();
656                 assert_eq!(ping, PingPong::Ping);
657                 let pong: PingPong = cmd_reader.recv();
658                 assert_eq!(pong, PingPong::Pong);
659 
660                 response_writer.send(&PingPong::Pong);
661                 response_writer.send(&PingPong::Ping);
662 
663                 test_result_clone
664             })
665             .unwrap()
666         };
667 
668         // Send one ping.
669         child_handle.send(&PingPong::Ping);
670 
671         // Expect one pong.
672         let pong = child_handle.recv();
673         assert_eq!(pong, PingPong::Pong);
674 
675         // Send ping and pong.
676         child_handle.send(&PingPong::Ping);
677         child_handle.send(&PingPong::Pong);
678 
679         // Expect pong and ping.
680         let pong = child_handle.recv();
681         assert_eq!(pong, PingPong::Pong);
682         let ping = child_handle.recv();
683         assert_eq!(ping, PingPong::Ping);
684 
685         assert_eq!(child_handle.get_result(), test_result);
686     }
687 }
688