capsules_core/test/
double_grant_entry.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Test that tries to enter a grant twice.
6//!
7//! This must fail or Tock allows multiple mutable references to the same memory
8//! which is undefined behavior.
9//!
10//! To use, setup this capsule and connect the syscall `Driver` implementation
11//! to userspace. Then call the commands to test various double grant entries.
12//!
13//! # Usage
14//!
15//! Here is my example usage for hail:
16//!
17//! ```diff
18//! diff --git a/boards/hail/src/main.rs b/boards/hail/src/main.rs
19//! index 110d45fa7..e8f4728c2 100644
20//! --- a/boards/hail/src/main.rs
21//! +++ b/boards/hail/src/main.rs
22//! @@ -73,6 +73,7 @@ struct Hail {
23//!      ipc: kernel::ipc::IPC<NUM_PROCS>,
24//!      crc: &'static capsules::crc::Crc<'static, sam4l::crccu::Crccu<'static>>,
25//!      dac: &'static capsules::dac::Dac<'static>,
26//! +    dge: &'static capsules::test::double_grant_entry::TestGrantDoubleEntry,
27//!  }
28//!
29//!  /// Mapping of integer syscalls to objects that implement syscalls.
30//! @@ -102,6 +103,8 @@ impl Platform for Hail {
31//!
32//!              capsules::dac::DRIVER_NUM => f(Some(self.dac)),
33//!
34//! +            capsules::test::double_grant_entry::DRIVER_NUM => f(Some(self.dge)),
35//! +
36//!              kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
37//!              _ => f(None),
38//!          }
39//! @@ -396,6 +399,14 @@ pub unsafe fn reset_handler() {
40//!          capsules::dac::Dac::new(&peripherals.dac)
41//!      );
42//!
43//! +    // Test double grant entry
44//! +    let dge = static_init!(
45//! +        capsules::test::double_grant_entry::TestGrantDoubleEntry,
46//! +        capsules::test::double_grant_entry::TestGrantDoubleEntry::new(
47//! +            board_kernel.create_grant(&memory_allocation_capability)
48//! +        )
49//! +    );
50//! +
51//!      // // DEBUG Restart All Apps
52//!      // //
53//!      // // Uncomment to enable a button press to restart all apps.
54//! @@ -440,6 +451,7 @@ pub unsafe fn reset_handler() {
55//!          ipc: kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability),
56//!          crc,
57//!          dac,
58//! +        dge,
59//!      };
60//!
61//!      // Setup the UART bus for nRF51 serialization..
62//!     ```
63
64use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
65use kernel::syscall::{CommandReturn, SyscallDriver};
66use kernel::{ErrorCode, ProcessId};
67
68/// Syscall driver number.
69pub const DRIVER_NUM: usize = 0xF001;
70
71/// Need a grant for the process.
72#[derive(Default)]
73pub struct App {
74    pending: bool,
75}
76
77pub struct TestGrantDoubleEntry {
78    grant: Grant<App, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
79}
80
81impl TestGrantDoubleEntry {
82    pub fn new(
83        grant: Grant<App, UpcallCount<0>, AllowRoCount<0>, AllowRwCount<0>>,
84    ) -> TestGrantDoubleEntry {
85        TestGrantDoubleEntry { grant }
86    }
87}
88
89impl SyscallDriver for TestGrantDoubleEntry {
90    fn command(
91        &self,
92        command_num: usize,
93        _: usize,
94        _: usize,
95        processid: ProcessId,
96    ) -> CommandReturn {
97        match command_num {
98            0 => CommandReturn::success(),
99
100            1 => {
101                // Try Grant.enter() then Grant.iter().enter()
102
103                // Check if we saw a grant with pending as true. If so, we
104                // entered the same grant twice.
105                let mut found_pending = false;
106
107                // Enter the grant for the app.
108                let err = self
109                    .grant
110                    .enter(processid, |appgrant, _| {
111                        // We can now change the state of the app's grant
112                        // region.
113                        appgrant.pending = true;
114
115                        // Now, try to iterate all grant regions.
116                        for grant in self.grant.iter() {
117                            // And, try to enter each grant! This should fail.
118                            grant.enter(|appgrant2, _| {
119                                if appgrant2.pending {
120                                    found_pending = true;
121                                }
122                            });
123                        }
124                        CommandReturn::success()
125                    })
126                    .unwrap_or_else(|err| err.into());
127
128                // If found pending is true, things are broken.
129                if found_pending {
130                    kernel::debug!("ERROR! Entered a grant twice simultaneously!!");
131                }
132
133                err
134            }
135
136            2 => {
137                // Try Grant.iter() then Grant.enter() then Grant.iter().enter()
138
139                // Check if we saw a grant with pending as true. If so, we
140                // entered the same grant twice.
141                let mut found_pending = false;
142
143                // Make sure the grant is allocated.
144                let _ = self.grant.enter(processid, |appgrant, _| {
145                    appgrant.pending = false;
146                });
147
148                for app in self.grant.iter() {
149                    let _ = self.grant.enter(processid, |appgrant, _| {
150                        // Mark the field.
151                        appgrant.pending = true;
152
153                        // Check if we can access this grant twice.
154                        app.enter(|appgrant2, _| {
155                            if appgrant2.pending {
156                                found_pending = true;
157                            }
158                        });
159                    });
160                }
161
162                // If found pending is true, things are broken.
163                if found_pending {
164                    kernel::debug!("ERROR! Entered a grant twice simultaneously!!");
165                }
166
167                // Since we expect a panic these return values don't matter.
168                CommandReturn::success()
169            }
170
171            3 => {
172                // Try Grant.enter() then Grant.enter()
173
174                // Check if we saw a grant with pending as true. If so, we
175                // entered the same grant twice.
176                let mut found_pending = false;
177
178                let _ = self.grant.enter(processid, |appgrant, _| {
179                    appgrant.pending = true;
180
181                    let _ = self.grant.enter(processid, |appgrant2, _| {
182                        if appgrant2.pending {
183                            found_pending = true;
184                        }
185                    });
186                });
187
188                // If found pending is true, things are broken.
189                if found_pending {
190                    kernel::debug!("ERROR! Entered a grant twice simultaneously!!");
191                }
192
193                // Since we expect a panic these return values don't matter.
194                CommandReturn::success()
195            }
196
197            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
198        }
199    }
200
201    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
202        self.grant.enter(processid, |_, _| {})
203    }
204}