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::PolicyValidationContext;
6use super::arrays::SimpleArrayView;
7use super::parser::{PolicyCursor, PolicyData, PolicyOffset};
8use crate::policy::{Counted, Parse, Validate};
9use hashbrown::hash_table::HashTable;
10use std::fmt::Debug;
11use std::hash::{DefaultHasher, Hash, Hasher};
12use std::marker::PhantomData;
13use zerocopy::FromBytes;
14
15/// A trait for types that have metadata.
16///
17/// Many policy objects have a fixed-sized metadata section that is much faster to parse than the
18/// full object. This trait is used when walking the binary policy to find objects of interest
19/// efficiently.
20pub trait HasMetadata {
21    /// The Rust type that represents the metadata.
22    type Metadata: FromBytes + Sized;
23}
24
25/// A trait for types that can be walked through the policy data.
26///
27/// This trait is used when walking the binary policy to find objects of interest efficiently.
28pub trait Walk {
29    /// Walks the policy data to the next object of the given type.
30    ///
31    /// Returns an error if the cursor cannot be walked to the next object of the given type.
32    fn walk(policy_data: &PolicyData, offset: PolicyOffset) -> PolicyOffset;
33}
34
35/// A view into a policy object.
36///
37/// This struct contains the start and end offsets of the object in the policy data. To read the
38/// object, use [`View::read`].
39#[derive(Debug, Clone, Copy)]
40pub struct View<T> {
41    phantom: PhantomData<T>,
42
43    /// The start offset of the object in the policy data.
44    start: PolicyOffset,
45
46    /// The end offset of the object in the policy data.
47    end: PolicyOffset,
48}
49
50impl<T> View<T> {
51    /// Creates a new view from the start and end offsets.
52    pub fn new(start: PolicyOffset, end: PolicyOffset) -> Self {
53        Self { phantom: PhantomData, start, end }
54    }
55
56    /// The start offset of the object in the policy data.
57    fn start(&self) -> PolicyOffset {
58        self.start
59    }
60}
61
62impl<T: Sized> View<T> {
63    /// Creates a new view at the given start offset.
64    ///
65    /// The end offset is calculated as the start offset plus the size of the object.
66    pub fn at(start: PolicyOffset) -> Self {
67        let end = start + std::mem::size_of::<T>() as u32;
68        Self::new(start, end)
69    }
70}
71
72impl<T: FromBytes + Sized> View<T> {
73    /// Reads the object from the policy data.
74    ///
75    /// This function requires the object to have a fixed size and simply copies the object from
76    /// the policy data.
77    ///
78    /// For variable-sized objects, use [`View::parse`] instead.
79    pub fn read(&self, policy_data: &PolicyData) -> T {
80        debug_assert_eq!(self.end - self.start, std::mem::size_of::<T>() as u32);
81        let start = self.start as usize;
82        let end = self.end as usize;
83        T::read_from_bytes(&policy_data[start..end]).unwrap()
84    }
85}
86
87impl<T: HasMetadata> View<T> {
88    /// Returns a view into the metadata of the object.
89    ///
90    /// Assumes the metadata is at the start of the object.
91    pub fn metadata(&self) -> View<T::Metadata> {
92        View::<T::Metadata>::at(self.start)
93    }
94
95    /// Reads the metadata from the policy data.
96    pub fn read_metadata(&self, policy_data: &PolicyData) -> T::Metadata {
97        self.metadata().read(policy_data)
98    }
99}
100
101impl<T: Parse> View<T> {
102    /// Parses the object from the policy data.
103    ///
104    /// This function uses the [`Parse`] trait to parse the object from the policy data.
105    ///
106    /// If the object has a fixed size, prefer [`View::read`] instead.
107    pub fn parse(&self, policy_data: &PolicyData) -> T {
108        let cursor = PolicyCursor::new_at(policy_data.clone(), self.start);
109        let (object, _) =
110            T::parse(cursor).map_err(Into::<anyhow::Error>::into).expect("policy should be valid");
111        object
112    }
113}
114
115impl<T: Validate + Parse> Validate for View<T> {
116    type Error = anyhow::Error;
117
118    fn validate(&self, context: &mut PolicyValidationContext) -> Result<(), Self::Error> {
119        let object = self.parse(&context.data);
120        object.validate(context).map_err(Into::<anyhow::Error>::into)
121    }
122}
123
124/// A view into the data of an array of objects.
125///
126/// This struct contains the start offset of the array and the number of objects in the array.
127/// To iterate over the objects, use [`ArrayDataView::iter`].
128#[derive(Debug, Clone, Copy)]
129pub struct ArrayDataView<D> {
130    phantom: PhantomData<D>,
131    start: PolicyOffset,
132    count: u32,
133}
134
135impl<D> ArrayDataView<D> {
136    /// Creates a new array data view from the start offset and count.
137    pub fn new(start: PolicyOffset, count: u32) -> Self {
138        Self { phantom: PhantomData, start, count }
139    }
140
141    /// Iterates over the objects in the array.
142    ///
143    /// The iterator returns views into the objects in the array.
144    ///
145    /// This function requires the policy data to be provided to the iterator because objects in
146    /// the array may have variable size.
147    pub fn iter(self, policy_data: &PolicyData) -> ArrayDataViewIter<D> {
148        ArrayDataViewIter::new(policy_data.clone(), self.start, self.count)
149    }
150}
151
152/// An iterator over the objects in an array.
153///
154/// This struct contains the cursor to the start of the array and the number of objects remaining
155/// to be iterated over.
156pub struct ArrayDataViewIter<D> {
157    phantom: PhantomData<D>,
158    policy_data: PolicyData,
159    offset: PolicyOffset,
160    remaining: u32,
161}
162
163impl<T> ArrayDataViewIter<T> {
164    /// Creates a new array data view iterator from the start cursor and remaining count.
165    pub(crate) fn new(policy_data: PolicyData, offset: PolicyOffset, remaining: u32) -> Self {
166        Self { phantom: PhantomData, policy_data, offset, remaining }
167    }
168}
169
170impl<D: Walk> std::iter::Iterator for ArrayDataViewIter<D> {
171    type Item = View<D>;
172
173    fn next(&mut self) -> Option<Self::Item> {
174        if self.remaining > 0 {
175            let start = self.offset;
176            self.offset = D::walk(&self.policy_data, start);
177            self.remaining -= 1;
178            Some(View::new(start, self.offset))
179        } else {
180            None
181        }
182    }
183}
184
185/// A view into the data of an array of objects.
186///
187/// This struct contains the start offset of the array and the number of objects in the array.
188/// To access the objects in the array, use [`ArrayView::data`].
189#[derive(Debug, Clone, Copy)]
190pub(crate) struct ArrayView<M, D> {
191    phantom: PhantomData<(M, D)>,
192    start: PolicyOffset,
193    count: u32,
194}
195
196impl<M, D> ArrayView<M, D> {
197    /// Creates a new array view from the start offset and count.
198    pub fn new(start: PolicyOffset, count: u32) -> Self {
199        Self { phantom: PhantomData, start, count }
200    }
201}
202
203impl<M: Sized, D> ArrayView<M, D> {
204    /// Returns a view into the metadata of the array.
205    pub fn metadata(&self) -> View<M> {
206        View::<M>::at(self.start)
207    }
208
209    /// Returns a view into the data of the array.
210    pub fn data(&self) -> ArrayDataView<D> {
211        ArrayDataView::new(self.metadata().end, self.count)
212    }
213}
214
215fn parse_array_data<D: Parse>(
216    cursor: PolicyCursor,
217    count: u32,
218) -> Result<PolicyCursor, anyhow::Error> {
219    let mut tail = cursor;
220    for _ in 0..count {
221        let (_, next) = D::parse(tail).map_err(Into::<anyhow::Error>::into)?;
222        tail = next;
223    }
224    Ok(tail)
225}
226
227impl<M: Counted + Parse + Sized, D: Parse> Parse for ArrayView<M, D> {
228    /// [`ArrayView`] abstracts over two types (`M` and `D`) that may have different [`Parse::Error`]
229    /// types. Unify error return type via [`anyhow::Error`].
230    type Error = anyhow::Error;
231
232    fn parse(cursor: PolicyCursor) -> Result<(Self, PolicyCursor), Self::Error> {
233        let start = cursor.offset();
234        let (metadata, cursor) = M::parse(cursor).map_err(Into::<anyhow::Error>::into)?;
235        let count = metadata.count();
236        let cursor = parse_array_data::<D>(cursor, count)?;
237        Ok((Self::new(start, count), cursor))
238    }
239}
240
241/// An iterator giving views of the objects in the underlying [`policy_data`] found from
242/// a single entry of a `HashedArrayView`.
243struct HashedArrayViewEntryIter<'a, D: HasMetadata> {
244    policy_data: &'a PolicyData,
245    limit: PolicyOffset,
246    metadata: D::Metadata,
247    offset: Option<PolicyOffset>,
248}
249
250impl<'a, D: HasMetadata + Walk> Iterator for HashedArrayViewEntryIter<'a, D>
251where
252    D::Metadata: Eq,
253{
254    type Item = View<D>;
255
256    fn next(&mut self) -> Option<Self::Item> {
257        if let Some(offset) = self.offset
258            && offset < self.limit
259        {
260            let element = View::<D>::at(offset);
261            let metadata = element.read_metadata(&self.policy_data);
262            if metadata == self.metadata {
263                self.offset = Some(D::walk(&self.policy_data, offset));
264                Some(element)
265            } else {
266                self.offset = None;
267                None
268            }
269        } else {
270            None
271        }
272    }
273}
274
275/// A view into the data of an array of objects, with efficient lookup based on metadata hash.
276///
277/// This struct contains only a vector of offsets into the policy data, to allow efficient lookup
278/// of vector elements with matching metadata.
279#[derive(Debug, Clone)]
280pub(crate) struct HashedArrayView<D: HasMetadata> {
281    phantom: PhantomData<D>,
282    index: HashTable<PolicyOffset>,
283    /// The offset in the policy data at which the elements indexed by this [`HashedArrayView`]
284    /// end. Iteration of elements by this [`HashedArrayView`] must not look for an element at
285    /// or beyond this offset.
286    limit: PolicyOffset,
287}
288
289impl<D: HasMetadata> HashedArrayView<D>
290where
291    D::Metadata: Hash,
292{
293    fn metadata_hash(metadata: &D::Metadata) -> u64 {
294        let mut hasher = DefaultHasher::new();
295        metadata.hash(&mut hasher);
296        hasher.finish()
297    }
298}
299
300impl<D: Parse + HasMetadata + Walk> HashedArrayView<D>
301where
302    D::Metadata: Eq + PartialEq + Hash + Debug,
303{
304    /// Looks up the entry with the specified metadata `key`, and parsed & returns the value.
305    /// This method is only appropriate to call when expecting to find at most one entry for
306    /// `key`; if there is a possibility of more than one element in the underlying
307    /// `policy_data` being associated with `key`, call `iterate` instead.
308    pub fn find(&self, key: D::Metadata, policy_data: &PolicyData) -> Option<D> {
309        let key_hash = Self::metadata_hash(&key);
310        let offset = self.index.find(key_hash, |&offset| {
311            let element = View::<D>::at(offset);
312            key == element.read_metadata(policy_data)
313        })?;
314        let element = View::<D>::at(*offset);
315        Some(element.parse(policy_data))
316    }
317
318    /// Looks up all entries with the specified metadata `key` and parses and emits those
319    /// values.
320    pub(super) fn iterate(
321        &self,
322        key: D::Metadata,
323        policy_data: &PolicyData,
324    ) -> impl Iterator<Item = D> {
325        let key_hash = Self::metadata_hash(&key);
326        let offset = self.index.find(key_hash, |&offset| {
327            let element = View::<D>::at(offset);
328            key == element.read_metadata(policy_data)
329        });
330        (HashedArrayViewEntryIter {
331            policy_data: policy_data,
332            limit: self.limit,
333            metadata: key,
334            offset: offset.cloned(),
335        })
336        .map(|element| element.parse(policy_data))
337    }
338
339    /// Emits a view for each reachable element.
340    pub(super) fn iterate_all(&self, policy_data: &PolicyData) -> impl Iterator<Item = View<D>> {
341        self.index
342            .iter()
343            .map(|offset| {
344                let element = View::<D>::at(*offset);
345                HashedArrayViewEntryIter {
346                    policy_data: policy_data,
347                    limit: self.limit,
348                    metadata: element.read_metadata(policy_data),
349                    offset: Some(*offset),
350                }
351            })
352            .flatten()
353    }
354}
355
356impl<D: Parse + HasMetadata + Walk> Parse for HashedArrayView<D>
357where
358    D::Metadata: Eq + Debug + PartialEq + Parse + Hash,
359{
360    type Error = anyhow::Error;
361
362    fn parse(cursor: PolicyCursor) -> Result<(Self, PolicyCursor), Self::Error> {
363        let (array_view, cursor) = SimpleArrayView::<D>::parse(cursor)?;
364
365        // Allocate a hash table sized appropriately for the array size.
366        let mut index = HashTable::with_capacity(array_view.count as usize);
367
368        // Record the offset at which the last array element ends.
369        let limit = cursor.offset();
370
371        // Iterate over the elements inserting the first offset at which each is
372        // seen into a hash bucket.
373        for view in array_view.data().iter(cursor.data()) {
374            let metadata = view.read_metadata(cursor.data());
375
376            index
377                .entry(
378                    Self::metadata_hash(&metadata),
379                    |&offset| {
380                        let element = View::<D>::at(offset);
381                        metadata == element.read_metadata(cursor.data())
382                    },
383                    |&offset| {
384                        let element = View::<D>::at(offset);
385                        Self::metadata_hash(&element.read_metadata(cursor.data()))
386                    },
387                )
388                .or_insert(view.start());
389        }
390
391        Ok((Self { phantom: PhantomData, index, limit }, cursor))
392    }
393}
394
395impl<D: Validate + Parse + HasMetadata + Walk> Validate for HashedArrayView<D>
396where
397    D::Metadata: Eq,
398{
399    type Error = anyhow::Error;
400
401    fn validate(&self, context: &mut PolicyValidationContext) -> Result<(), Self::Error> {
402        let policy_data = context.data.clone();
403        for element in self
404            .index
405            .iter()
406            .map(|offset| {
407                let element = View::<D>::at(*offset);
408                HashedArrayViewEntryIter::<D> {
409                    policy_data: &policy_data,
410                    limit: self.limit,
411                    metadata: element.read_metadata(&policy_data),
412                    offset: Some(*offset),
413                }
414            })
415            .flatten()
416        {
417            element.validate(context)?;
418        }
419
420        Ok(())
421    }
422}