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::parser::{PolicyCursor, PolicyData, PolicyOffset};
7use crate::policy::{Counted, Parse, Validate};
8use std::marker::PhantomData;
9use zerocopy::FromBytes;
10
11/// A trait for types that have metadata.
12///
13/// Many policy objects have a fixed-sized metadata section that is much faster to parse than the
14/// full object. This trait is used when walking the binary policy to find objects of interest
15/// efficiently.
16pub trait HasMetadata {
17    /// The Rust type that represents the metadata.
18    type Metadata: FromBytes + Sized;
19}
20
21/// A trait for types that can be walked through the policy data.
22///
23/// This trait is used when walking the binary policy to find objects of interest efficiently.
24pub trait Walk {
25    /// Walks the policy data to the next object of the given type.
26    ///
27    /// Returns an error if the cursor cannot be walked to the next object of the given type.
28    fn walk(policy_data: &PolicyData, offset: PolicyOffset) -> PolicyOffset;
29}
30
31/// A view into a policy object.
32///
33/// This struct contains the start and end offsets of the object in the policy data. To read the
34/// object, use [`View::read`].
35#[derive(Debug, Clone, Copy)]
36pub struct View<T> {
37    phantom: PhantomData<T>,
38
39    /// The start offset of the object in the policy data.
40    start: PolicyOffset,
41
42    /// The end offset of the object in the policy data.
43    end: PolicyOffset,
44}
45
46impl<T> View<T> {
47    /// Creates a new view from the start and end offsets.
48    pub fn new(start: PolicyOffset, end: PolicyOffset) -> Self {
49        Self { phantom: PhantomData, start, end }
50    }
51}
52
53impl<T: Sized> View<T> {
54    /// Creates a new view at the given start offset.
55    ///
56    /// The end offset is calculated as the start offset plus the size of the object.
57    pub fn at(start: PolicyOffset) -> Self {
58        let end = start + std::mem::size_of::<T>() as u32;
59        Self::new(start, end)
60    }
61}
62
63impl<T: FromBytes + Sized> View<T> {
64    /// Reads the object from the policy data.
65    ///
66    /// This function requires the object to have a fixed size and simply copies the object from
67    /// the policy data.
68    ///
69    /// For variable-sized objects, use [`View::parse`] instead.
70    pub fn read(&self, policy_data: &PolicyData) -> T {
71        debug_assert_eq!(self.end - self.start, std::mem::size_of::<T>() as u32);
72        let start = self.start as usize;
73        let end = self.end as usize;
74        T::read_from_bytes(&policy_data[start..end]).unwrap()
75    }
76}
77
78impl<T: HasMetadata> View<T> {
79    /// Returns a view into the metadata of the object.
80    ///
81    /// Assumes the metadata is at the start of the object.
82    pub fn metadata(&self) -> View<T::Metadata> {
83        View::<T::Metadata>::at(self.start)
84    }
85
86    /// Reads the metadata from the policy data.
87    pub fn read_metadata(&self, policy_data: &PolicyData) -> T::Metadata {
88        self.metadata().read(policy_data)
89    }
90}
91
92impl<T: Parse> View<T> {
93    /// Parses the object from the policy data.
94    ///
95    /// This function uses the [`Parse`] trait to parse the object from the policy data.
96    ///
97    /// If the object has a fixed size, prefer [`View::read`] instead.
98    pub fn parse(&self, policy_data: &PolicyData) -> T {
99        let cursor = PolicyCursor::new_at(policy_data.clone(), self.start);
100        let (object, _) =
101            T::parse(cursor).map_err(Into::<anyhow::Error>::into).expect("policy should be valid");
102        object
103    }
104}
105
106impl<T: Validate + Parse> Validate for View<T> {
107    type Error = anyhow::Error;
108
109    fn validate(&self, context: &mut PolicyValidationContext) -> Result<(), Self::Error> {
110        let object = self.parse(&context.data);
111        object.validate(context).map_err(Into::<anyhow::Error>::into)
112    }
113}
114
115/// A view into the data of an array of objects.
116///
117/// This struct contains the start offset of the array and the number of objects in the array.
118/// To iterate over the objects, use [`ArrayDataView::iter`].
119#[derive(Debug, Clone, Copy)]
120pub struct ArrayDataView<D> {
121    phantom: PhantomData<D>,
122    start: PolicyOffset,
123    count: u32,
124}
125
126impl<D> ArrayDataView<D> {
127    /// Creates a new array data view from the start offset and count.
128    pub fn new(start: PolicyOffset, count: u32) -> Self {
129        Self { phantom: PhantomData, start, count }
130    }
131
132    /// Iterates over the objects in the array.
133    ///
134    /// The iterator returns views into the objects in the array.
135    ///
136    /// This function requires the policy data to be provided to the iterator because objects in
137    /// the array may have variable size.
138    pub fn iter(self, policy_data: &PolicyData) -> ArrayDataViewIter<D> {
139        ArrayDataViewIter::new(policy_data.clone(), self.start, self.count)
140    }
141}
142
143/// An iterator over the objects in an array.
144///
145/// This struct contains the cursor to the start of the array and the number of objects remaining
146/// to be iterated over.
147pub struct ArrayDataViewIter<D> {
148    phantom: PhantomData<D>,
149    policy_data: PolicyData,
150    offset: PolicyOffset,
151    remaining: u32,
152}
153
154impl<T> ArrayDataViewIter<T> {
155    /// Creates a new array data view iterator from the start cursor and remaining count.
156    pub(crate) fn new(policy_data: PolicyData, offset: PolicyOffset, remaining: u32) -> Self {
157        Self { phantom: PhantomData, policy_data, offset, remaining }
158    }
159}
160
161impl<D: Walk> std::iter::Iterator for ArrayDataViewIter<D> {
162    type Item = View<D>;
163
164    fn next(&mut self) -> Option<Self::Item> {
165        if self.remaining > 0 {
166            let start = self.offset;
167            self.offset = D::walk(&self.policy_data, start);
168            self.remaining -= 1;
169            Some(View::new(start, self.offset))
170        } else {
171            None
172        }
173    }
174}
175
176/// A view into the data of an array of objects.
177///
178/// This struct contains the start offset of the array and the number of objects in the array.
179/// To access the objects in the array, use [`ArrayView::data`].
180#[derive(Debug, Clone, Copy)]
181pub(crate) struct ArrayView<M, D> {
182    phantom: PhantomData<(M, D)>,
183    start: PolicyOffset,
184    count: u32,
185}
186
187impl<M, D> ArrayView<M, D> {
188    /// Creates a new array view from the start offset and count.
189    pub fn new(start: PolicyOffset, count: u32) -> Self {
190        Self { phantom: PhantomData, start, count }
191    }
192}
193
194impl<M: Sized, D> ArrayView<M, D> {
195    /// Returns a view into the metadata of the array.
196    pub fn metadata(&self) -> View<M> {
197        View::<M>::at(self.start)
198    }
199
200    /// Returns a view into the data of the array.
201    pub fn data(&self) -> ArrayDataView<D> {
202        ArrayDataView::new(self.metadata().end, self.count)
203    }
204}
205
206fn parse_array_data<D: Parse>(
207    cursor: PolicyCursor,
208    count: u32,
209) -> Result<PolicyCursor, anyhow::Error> {
210    let mut tail = cursor;
211    for _ in 0..count {
212        let (_, next) = D::parse(tail).map_err(Into::<anyhow::Error>::into)?;
213        tail = next;
214    }
215    Ok(tail)
216}
217
218impl<M: Counted + Parse + Sized, D: Parse> Parse for ArrayView<M, D> {
219    /// [`ArrayView`] abstracts over two types (`M` and `D`) that may have different [`Parse::Error`]
220    /// types. Unify error return type via [`anyhow::Error`].
221    type Error = anyhow::Error;
222
223    fn parse(cursor: PolicyCursor) -> Result<(Self, PolicyCursor), Self::Error> {
224        let start = cursor.offset();
225        let (metadata, cursor) = M::parse(cursor).map_err(Into::<anyhow::Error>::into)?;
226        let count = metadata.count();
227        let cursor = parse_array_data::<D>(cursor, count)?;
228        Ok((Self::new(start, count), cursor))
229    }
230}