Skip to main content

selinux/policy/
view.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::arrays::SimpleArrayView;
6use super::parser::{PolicyCursor, PolicyData, PolicyOffset};
7use super::{Counted, Parse, PolicyValidationContext, Validate};
8
9use hashbrown::hash_table::HashTable;
10use rapidhash::RapidHasher;
11use std::fmt::Debug;
12use std::hash::{Hash, Hasher};
13use std::marker::PhantomData;
14use zerocopy::{FromBytes, little_endian as le};
15
16/// A trait for types that have metadata.
17///
18/// Many policy objects have a fixed-sized metadata section that is much faster to parse than the
19/// full object. This trait is used when walking the binary policy to find objects of interest
20/// efficiently.
21pub trait HasMetadata {
22    /// The Rust type that represents the metadata.
23    type Metadata: FromBytes + Sized;
24}
25
26/// A trait for types that can be walked through the policy data.
27///
28/// This trait is used when walking the binary policy to find objects of interest efficiently.
29pub trait Walk {
30    /// Walks the policy data to the next object of the given type.
31    ///
32    /// Returns an error if the cursor cannot be walked to the next object of the given type.
33    fn walk(policy_data: &PolicyData, offset: PolicyOffset) -> PolicyOffset;
34}
35
36/// A view into a policy object.
37///
38/// This struct contains the start and end offsets of the object in the policy data. To read the
39/// object, use [`View::read`].
40#[derive(Debug, Clone, Copy)]
41pub struct View<T> {
42    phantom: PhantomData<T>,
43
44    /// The start offset of the object in the policy data.
45    start: PolicyOffset,
46
47    /// The end offset of the object in the policy data.
48    end: PolicyOffset,
49}
50
51impl<T> View<T> {
52    /// Creates a new view from the start and end offsets.
53    pub fn new(start: PolicyOffset, end: PolicyOffset) -> Self {
54        Self { phantom: PhantomData, start, end }
55    }
56}
57
58impl<T: Sized> View<T> {
59    /// Creates a new view at the given start offset.
60    ///
61    /// The end offset is calculated as the start offset plus the size of the object.
62    pub fn at(start: PolicyOffset) -> Self {
63        let end = start + std::mem::size_of::<T>() as u32;
64        Self::new(start, end)
65    }
66}
67
68impl<T: FromBytes + Sized> View<T> {
69    /// Reads the object from the policy data.
70    ///
71    /// This function requires the object to have a fixed size and simply copies the object from
72    /// the policy data.
73    ///
74    /// For variable-sized objects, use [`View::parse`] instead.
75    pub fn read(&self, policy_data: &PolicyData) -> T {
76        debug_assert_eq!(self.end - self.start, std::mem::size_of::<T>() as u32);
77        let start = self.start as usize;
78        let end = self.end as usize;
79        T::read_from_bytes(&policy_data[start..end]).unwrap()
80    }
81}
82
83impl<T: HasMetadata> View<T> {
84    /// Returns a view into the metadata of the object.
85    ///
86    /// Assumes the metadata is at the start of the object.
87    pub fn metadata(&self) -> View<T::Metadata> {
88        View::<T::Metadata>::at(self.start)
89    }
90
91    /// Reads the metadata from the policy data.
92    pub fn read_metadata(&self, policy_data: &PolicyData) -> T::Metadata {
93        self.metadata().read(policy_data)
94    }
95}
96
97impl<T: Parse> View<T> {
98    /// Parses the object from the policy data.
99    ///
100    /// This function uses the [`Parse`] trait to parse the object from the policy data.
101    ///
102    /// If the object has a fixed size, prefer [`View::read`] instead.
103    pub fn parse(&self, policy_data: &PolicyData) -> T {
104        let cursor = PolicyCursor::new_at(policy_data, self.start);
105        let (object, _) =
106            T::parse(cursor).map_err(Into::<anyhow::Error>::into).expect("policy should be valid");
107        object
108    }
109}
110
111impl<T: Validate + Parse> Validate for View<T> {
112    type Error = anyhow::Error;
113
114    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
115        let object = self.parse(&context.data);
116        object.validate(context).map_err(Into::<anyhow::Error>::into)
117    }
118}
119
120/// A view into the data of an array of objects.
121///
122/// This struct contains the start offset of the array and the number of objects in the array.
123/// To iterate over the objects, use [`ArrayDataView::iter`].
124#[derive(Debug, Clone, Copy)]
125pub struct ArrayDataView<D> {
126    phantom: PhantomData<D>,
127    start: PolicyOffset,
128    count: u32,
129}
130
131impl<D> ArrayDataView<D> {
132    /// Creates a new array data view from the start offset and count.
133    pub fn new(start: PolicyOffset, count: u32) -> Self {
134        Self { phantom: PhantomData, start, count }
135    }
136
137    /// Iterates over the objects in the array.
138    ///
139    /// The iterator returns views into the objects in the array.
140    ///
141    /// This function requires the policy data to be provided to the iterator because objects in
142    /// the array may have variable size.
143    pub fn iter(self, policy_data: &PolicyData) -> ArrayDataViewIter<D> {
144        ArrayDataViewIter::new(policy_data.clone(), self.start, self.count)
145    }
146}
147
148/// An iterator over the objects in an array.
149///
150/// This struct contains the cursor to the start of the array and the number of objects remaining
151/// to be iterated over.
152pub struct ArrayDataViewIter<D> {
153    phantom: PhantomData<D>,
154    policy_data: PolicyData,
155    offset: PolicyOffset,
156    remaining: u32,
157}
158
159impl<T> ArrayDataViewIter<T> {
160    /// Creates a new array data view iterator from the start cursor and remaining count.
161    fn new(policy_data: PolicyData, offset: PolicyOffset, remaining: u32) -> Self {
162        Self { phantom: PhantomData, policy_data, offset, remaining }
163    }
164}
165
166impl<D: Walk> std::iter::Iterator for ArrayDataViewIter<D> {
167    type Item = View<D>;
168
169    fn next(&mut self) -> Option<Self::Item> {
170        if self.remaining > 0 {
171            let start = self.offset;
172            self.offset = D::walk(&self.policy_data, start);
173            self.remaining -= 1;
174            Some(View::new(start, self.offset))
175        } else {
176            None
177        }
178    }
179}
180
181/// A view into the data of an array of objects.
182///
183/// This struct contains the start offset of the array and the number of objects in the array.
184/// To access the objects in the array, use [`ArrayView::data`].
185#[derive(Debug, Clone, Copy)]
186pub(super) struct ArrayView<M, D> {
187    phantom: PhantomData<(M, D)>,
188    start: PolicyOffset,
189    count: u32,
190}
191
192impl<M, D> ArrayView<M, D> {
193    /// Creates a new array view from the start offset and count.
194    pub fn new(start: PolicyOffset, count: u32) -> Self {
195        Self { phantom: PhantomData, start, count }
196    }
197}
198
199impl<M: Sized, D> ArrayView<M, D> {
200    /// Returns a view into the metadata of the array.
201    pub fn metadata(&self) -> View<M> {
202        View::<M>::at(self.start)
203    }
204
205    /// Returns a view into the data of the array.
206    pub fn data(&self) -> ArrayDataView<D> {
207        ArrayDataView::new(self.metadata().end, self.count)
208    }
209}
210
211fn parse_array_data<'a, D: Parse>(
212    cursor: PolicyCursor<'a>,
213    count: u32,
214) -> Result<PolicyCursor<'a>, anyhow::Error> {
215    let mut tail = cursor;
216    for _ in 0..count {
217        let (_, next) = D::parse(tail).map_err(Into::<anyhow::Error>::into)?;
218        tail = next;
219    }
220    Ok(tail)
221}
222
223impl<M: Counted + Parse + Sized, D: Parse> Parse for ArrayView<M, D> {
224    /// [`ArrayView`] abstracts over two types (`M` and `D`) that may have different [`Parse::Error`]
225    /// types. Unify error return type via [`anyhow::Error`].
226    type Error = anyhow::Error;
227
228    fn parse<'a>(cursor: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
229        let start = cursor.offset();
230        let (metadata, cursor) = M::parse(cursor).map_err(Into::<anyhow::Error>::into)?;
231        let count = metadata.count();
232        let cursor = parse_array_data::<D>(cursor, count)?;
233        Ok((Self::new(start, count), cursor))
234    }
235}
236
237/// A trait for types that can be used as keys in a hash table.
238///
239/// This trait is used by [`HashedBlocksView`] to store and retrieve values.
240pub(super) trait Hashable {
241    type Key: Parse + Hash + Eq;
242    type Value: Parse + Walk;
243
244    /// Returns a reference to the key.
245    fn key(&self) -> &Self::Key;
246
247    /// Returns a [`SimpleArrayView`] into the values.
248    fn values(&self) -> &SimpleArrayView<Self::Value>;
249}
250
251/// Stores an mapping from a [`D::Key`] to a set of [`D::Value`]s
252#[derive(Debug, Clone)]
253pub(super) struct CustomKeyHashedView<D: Hashable> {
254    /// Stores the offset to D::Key.
255    index: HashTable<PolicyOffset>,
256    _phantom: PhantomData<D>,
257}
258
259impl<D: Hashable + Parse> CustomKeyHashedView<D> {
260    /// Returns an iterator over the entries with the specified `key` and parses and
261    /// emits those values.
262    pub(super) fn find_all(
263        &self,
264        query_key: D::Key,
265        policy_data: &PolicyData,
266    ) -> impl Iterator<Item = D::Value> {
267        let key_offset = self.index.find(compute_hash(&query_key), |&key_offset| {
268            let cursor = PolicyCursor::new_at(policy_data, key_offset);
269            let (key, _) = D::Key::parse(cursor)
270                .map_err(Into::<anyhow::Error>::into)
271                .expect("policy should be valid");
272
273            key == query_key
274        });
275
276        key_offset.into_iter().flat_map(move |&key_offset| {
277            let cursor = PolicyCursor::new_at(policy_data, key_offset);
278            let (entry, _) = D::parse(cursor)
279                .map_err(Into::<anyhow::Error>::into)
280                .expect("policy should be valid");
281
282            entry.values().data().iter(policy_data).map(move |v| v.parse(policy_data))
283        })
284    }
285
286    pub(super) fn iter<'a>(
287        &'a self,
288        policy_data: &'a PolicyData,
289    ) -> impl Iterator<Item = Result<D, anyhow::Error>> + 'a {
290        self.index.iter().map(move |&offset| {
291            let cursor = PolicyCursor::new_at(policy_data, offset);
292            let (entry, _) = D::parse(cursor).map_err(Into::<anyhow::Error>::into)?;
293            Ok(entry)
294        })
295    }
296}
297
298fn compute_hash<V: Hash>(val: &V) -> u64 {
299    let mut hasher = RapidHasher::default();
300    val.hash(&mut hasher);
301    hasher.finish()
302}
303
304impl<D: Hashable + Parse> Parse for CustomKeyHashedView<D> {
305    type Error = anyhow::Error;
306
307    /// Parses (D::Key, SimpleArrayView<D::Value>) entries and stores the keys into a CustomKeyHashedView.
308    fn parse<'a>(cursor: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
309        // Parse the count of entries.
310        let (metadata, cursor) = le::U32::parse(cursor).map_err(Into::<anyhow::Error>::into)?;
311        let count = metadata.count();
312
313        // The index will store [`count`] entries. Reserve the necessary capacity ahead to avoid resizing later on.
314        let mut index = HashTable::with_capacity(count as usize);
315
316        let mut key_offset = cursor.offset();
317        let mut tail = cursor;
318        for _ in 0..count {
319            let (entry, next) = D::parse(tail).map_err(Into::<anyhow::Error>::into)?;
320            tail = next;
321
322            let key: &D::Key = entry.key();
323            index.insert_unique(compute_hash(&key), key_offset, |&key_offset| {
324                let policy_cursor = PolicyCursor::new_at(tail.data(), key_offset);
325                let (key, _) = D::Key::parse(policy_cursor)
326                    .map_err(Into::<anyhow::Error>::into)
327                    .expect("policy should be valid");
328                compute_hash::<D::Key>(&key)
329            });
330            key_offset = tail.offset();
331        }
332
333        Ok((Self { _phantom: PhantomData, index }, tail))
334    }
335}
336
337impl<D: Hashable + Parse> Validate for CustomKeyHashedView<D>
338where
339    SimpleArrayView<D::Value>: Validate<Error = anyhow::Error>,
340{
341    type Error = anyhow::Error;
342
343    fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
344        for key_offset in self.index.iter() {
345            let cursor = PolicyCursor::new_at(&context.data, *key_offset);
346            let (entry, _) = D::parse(cursor).map_err(Into::<anyhow::Error>::into)?;
347
348            entry.values().validate(context)?;
349        }
350        Ok(())
351    }
352}