Skip to main content

selinux/new_policy/
types.rs

1// Copyright 2026 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 std::num::NonZeroU16;
6use std::ops::Index;
7
8use hashbrown::HashTable;
9use hashbrown::hash_table::Entry;
10use selinux_policy_derive::{HasPolicyId, Parse, Serialize, Validate};
11
12use super::bitmap::IdSet;
13use super::error::{ParseError, SerializeError, ValidateError};
14use super::id_type::IdType;
15use super::indexed::hash_name;
16use super::parser::{Array, PolicyCursor, PolicyWriter};
17use super::traits::{HasName, Parse, PolicyId, Serialize, Validate};
18use super::{NewPolicy, U24Index};
19
20/// Tag type for type safety of policy type identifiers.
21#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
22pub struct TypeTag;
23
24/// Identifies a type (or type attribute) within a policy.
25pub type TypeId = IdType<NonZeroU16, TypeTag>;
26
27/// Set of types that are marked permissive.
28pub type PermissiveTypeSet = IdSet<TypeId, true>;
29
30/// Set of [`TypeId`]s.
31pub type TypeSet = IdSet<TypeId>;
32
33impl Validate for TypeId {
34    fn validate(&self, policy: &NewPolicy) -> Result<(), ValidateError> {
35        policy
36            .types()
37            .get_by_id(*self)
38            .map(|_| ())
39            .ok_or_else(|| ValidateError::UnknownId { kind: "type", id: self.as_u32() })
40    }
41}
42
43/// Classification of a type symbol (alias, primary type, or attribute).
44#[derive(Copy, Clone, Debug, PartialEq, Eq, Parse, Serialize, Validate)]
45#[policy(wire_type = u32)]
46pub enum TypeKind {
47    Alias = 0,
48    Type = 1,
49    Attribute = 3,
50}
51
52/// Parsed SELinux type, containing an ID, a name, properties, and optional bounds.
53#[derive(Debug, Validate, HasPolicyId)]
54pub struct Type {
55    id: TypeId,
56    name: Box<[u8]>,
57    properties: TypeKind,
58    bounds: Option<TypeId>,
59}
60
61impl HasName for Type {
62    fn name(&self) -> &[u8] {
63        &self.name
64    }
65}
66
67impl Type {
68    pub fn bounded_by(&self) -> Option<TypeId> {
69        self.bounds
70    }
71}
72
73#[derive(Parse, Serialize)]
74struct BinaryTypeMetadata {
75    length: u32,
76    id: TypeId,
77    properties: TypeKind,
78    bounds: Option<TypeId>,
79}
80
81impl Parse for Type {
82    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
83        let metadata = BinaryTypeMetadata::parse(cursor)?;
84        let name = cursor.read_bytes(metadata.length as usize)?.to_vec().into_boxed_slice();
85
86        Ok(Self { id: metadata.id, name, properties: metadata.properties, bounds: metadata.bounds })
87    }
88}
89
90impl Serialize for Type {
91    fn serialize(&self, writer: &mut PolicyWriter<'_>) -> Result<(), SerializeError> {
92        let metadata = BinaryTypeMetadata {
93            length: self.name.len() as u32,
94            id: self.id,
95            properties: self.properties,
96            bounds: self.bounds,
97        };
98        metadata.serialize(writer)?;
99        writer.write_bytes(&self.name);
100        Ok(())
101    }
102}
103
104/// Container for all types in the policy, providing indices for fast lookup by ID and Name.
105#[derive(Debug)]
106pub struct Types {
107    primary_names_count: u32,
108    /// In-order list of all types, attributes, and aliases.
109    ordered: Array<Type>,
110
111    /// Maps TypeId -> index in `ordered`. Only contains Types and Attributes.
112    /// Index is `TypeId - 1`.
113    by_id: Box<[Option<U24Index>]>,
114
115    /// Maps name -> index in `ordered`. Only contains Types and Aliases.
116    by_name: HashTable<U24Index>,
117    hasher: rapidhash::RapidBuildHasher,
118}
119
120impl Parse for Types {
121    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
122        let primary_names_count = u32::parse(cursor)?;
123        cursor.set_types_count(primary_names_count);
124        let ordered = Array::<Type>::parse(cursor)?;
125
126        // Build indices
127        let mut by_id = Vec::with_capacity(ordered.len());
128        let hasher = rapidhash::RapidBuildHasher::default();
129        let mut by_name = HashTable::with_capacity(ordered.len());
130
131        for (index, item) in ordered.iter().enumerate() {
132            let u24_idx: U24Index = index.try_into()?;
133            if item.properties == TypeKind::Type || item.properties == TypeKind::Attribute {
134                let id = item.id.as_u32() as usize;
135                if id > by_id.len() {
136                    by_id.resize(id, None);
137                } else if by_id[id - 1].is_some() {
138                    return Err(ParseError::DuplicateId { id: item.id.as_u32() });
139                }
140                by_id[id - 1] = Some(u24_idx);
141            }
142            if item.properties == TypeKind::Type || item.properties == TypeKind::Alias {
143                let name = item.name.as_ref();
144                let hash = hash_name(&hasher, name);
145                let Entry::Vacant(entry) = by_name.entry(
146                    hash,
147                    |&idx| ordered[usize::from(idx)].name.as_ref() == name,
148                    |&idx| hash_name(&hasher, ordered[usize::from(idx)].name.as_ref()),
149                ) else {
150                    return Err(ParseError::DuplicateName { name: name.into() });
151                };
152                entry.insert(u24_idx);
153            }
154        }
155
156        by_id.shrink_to_fit();
157        by_name.shrink_to_fit(|&idx| hash_name(&hasher, ordered[usize::from(idx)].name.as_ref()));
158
159        Ok(Self { primary_names_count, ordered, by_id: by_id.into_boxed_slice(), by_name, hasher })
160    }
161}
162
163impl Serialize for Types {
164    fn serialize(&self, writer: &mut PolicyWriter<'_>) -> Result<(), SerializeError> {
165        self.primary_names_count.serialize(writer)?;
166        self.ordered.serialize(writer)
167    }
168}
169
170impl Validate for Types {
171    fn validate(&self, policy: &NewPolicy) -> Result<(), ValidateError> {
172        self.ordered.validate(policy)
173    }
174}
175
176impl Types {
177    pub fn primary_names_count(&self) -> u32 {
178        self.primary_names_count
179    }
180
181    pub fn get_by_id(&self, id: TypeId) -> Option<&Type> {
182        let index = self.by_id.get((id.as_u32() - 1) as usize)?.as_ref()?;
183        Some(&self.ordered[*index])
184    }
185
186    pub fn get_by_name(&self, name: &[u8]) -> Option<&Type> {
187        let hash = hash_name(&self.hasher, name);
188        let idx = self.by_name.find(hash, |&idx| self.ordered[idx].name.as_ref() == name)?;
189        Some(&self.ordered[*idx])
190    }
191
192    pub fn is_empty(&self) -> bool {
193        self.ordered.is_empty()
194    }
195
196    pub fn iter(&self) -> impl Iterator<Item = &Type> {
197        self.ordered.iter()
198    }
199}
200
201/// Type-to-attribute mappings for each primary type in the policy.
202#[derive(Clone, Debug, PartialEq, Eq, Serialize, Validate)]
203pub struct TypeAttributeMaps {
204    maps: Box<[TypeSet]>,
205}
206
207impl TypeAttributeMaps {
208    /// Returns the attribute [`TypeSet`] for the specified `type_id`.
209    pub fn get(&self, type_id: TypeId) -> Option<&TypeSet> {
210        self.maps.get((type_id.as_u32() - 1) as usize)
211    }
212}
213
214impl Index<TypeId> for TypeAttributeMaps {
215    type Output = TypeSet;
216
217    fn index(&self, id: TypeId) -> &Self::Output {
218        &self.maps[(id.as_u32() - 1) as usize]
219    }
220}
221
222impl Parse for TypeAttributeMaps {
223    fn parse(cursor: &mut PolicyCursor<'_>) -> Result<Self, ParseError> {
224        let count = cursor.types_count() as usize;
225        let mut maps = Vec::with_capacity(count);
226        for _ in 0..count {
227            maps.push(TypeSet::parse(cursor)?);
228        }
229        Ok(Self { maps: maps.into_boxed_slice() })
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236    use crate::new_policy::metadata::PolicyVersion;
237
238    #[test]
239    fn test_types_lookup() {
240        let mut bytes = Vec::new();
241        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut bytes);
242        2u32.serialize(&mut policy_writer).unwrap(); // primary_names_count
243        2u32.serialize(&mut policy_writer).unwrap(); // ordered count = 2
244
245        let t1 = Type {
246            id: TypeId::new(NonZeroU16::new(1).unwrap()),
247            name: Box::from(b"foo".as_slice()),
248            properties: TypeKind::Type,
249            bounds: None,
250        };
251        t1.serialize(&mut policy_writer).unwrap();
252
253        let t2 = Type {
254            id: TypeId::new(NonZeroU16::new(2).unwrap()),
255            name: Box::from(b"bar".as_slice()),
256            properties: TypeKind::Type,
257            bounds: None,
258        };
259        t2.serialize(&mut policy_writer).unwrap();
260
261        let mut cursor = PolicyCursor::new(&bytes);
262        let types = Types::parse(&mut cursor).expect("parse types");
263
264        assert_eq!(
265            types.get_by_id(TypeId::new(NonZeroU16::new(1).unwrap())).map(|t| t.name()),
266            Some(b"foo".as_slice())
267        );
268        assert_eq!(
269            types.get_by_id(TypeId::new(NonZeroU16::new(2).unwrap())).map(|t| t.name()),
270            Some(b"bar".as_slice())
271        );
272        assert!(types.get_by_id(TypeId::new(NonZeroU16::new(3).unwrap())).is_none());
273
274        assert_eq!(
275            types.get_by_name(b"foo").map(|t| t.id),
276            Some(TypeId::new(NonZeroU16::new(1).unwrap()))
277        );
278        assert_eq!(
279            types.get_by_name(b"bar").map(|t| t.id),
280            Some(TypeId::new(NonZeroU16::new(2).unwrap()))
281        );
282        assert!(types.get_by_name(b"baz").is_none());
283    }
284
285    #[test]
286    fn test_types_duplicate_id() {
287        let mut bytes = Vec::new();
288        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut bytes);
289        2u32.serialize(&mut policy_writer).unwrap(); // primary_names_count
290        2u32.serialize(&mut policy_writer).unwrap(); // ordered count = 2
291
292        let t1 = Type {
293            id: TypeId::new(NonZeroU16::new(1).unwrap()),
294            name: Box::from(b"foo".as_slice()),
295            properties: TypeKind::Type,
296            bounds: None,
297        };
298        t1.serialize(&mut policy_writer).unwrap();
299
300        let t2 = Type {
301            id: TypeId::new(NonZeroU16::new(1).unwrap()),
302            name: Box::from(b"bar".as_slice()),
303            properties: TypeKind::Type,
304            bounds: None,
305        };
306        t2.serialize(&mut policy_writer).unwrap();
307
308        let mut cursor = PolicyCursor::new(&bytes);
309        let result = Types::parse(&mut cursor);
310        assert!(matches!(result, Err(ParseError::DuplicateId { id: 1 })));
311    }
312
313    #[test]
314    fn test_types_duplicate_name() {
315        let mut bytes = Vec::new();
316        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut bytes);
317        2u32.serialize(&mut policy_writer).unwrap(); // primary_names_count
318        2u32.serialize(&mut policy_writer).unwrap(); // ordered count = 2
319
320        let t1 = Type {
321            id: TypeId::new(NonZeroU16::new(1).unwrap()),
322            name: Box::from(b"foo".as_slice()),
323            properties: TypeKind::Type,
324            bounds: None,
325        };
326        t1.serialize(&mut policy_writer).unwrap();
327
328        let t2 = Type {
329            id: TypeId::new(NonZeroU16::new(2).unwrap()),
330            name: Box::from(b"foo".as_slice()),
331            properties: TypeKind::Type,
332            bounds: None,
333        };
334        t2.serialize(&mut policy_writer).unwrap();
335
336        let mut cursor = PolicyCursor::new(&bytes);
337        let result = Types::parse(&mut cursor);
338        assert!(matches!(result, Err(ParseError::DuplicateName { name }) if name == b"foo"));
339    }
340
341    #[test]
342    fn test_types_duplicate_alias_name() {
343        let mut bytes = Vec::new();
344        let mut policy_writer = PolicyWriter::new(PolicyVersion::V33, &mut bytes);
345        1u32.serialize(&mut policy_writer).unwrap(); // primary_names_count
346        2u32.serialize(&mut policy_writer).unwrap(); // ordered count = 2
347
348        let t1 = Type {
349            id: TypeId::new(NonZeroU16::new(1).unwrap()),
350            name: Box::from(b"foo".as_slice()),
351            properties: TypeKind::Type,
352            bounds: None,
353        };
354        t1.serialize(&mut policy_writer).unwrap();
355
356        let t2 = Type {
357            id: TypeId::new(NonZeroU16::new(1).unwrap()),
358            name: Box::from(b"foo".as_slice()),
359            properties: TypeKind::Alias,
360            bounds: None,
361        };
362        t2.serialize(&mut policy_writer).unwrap();
363
364        let mut cursor = PolicyCursor::new(&bytes);
365        let result = Types::parse(&mut cursor);
366        assert!(matches!(result, Err(ParseError::DuplicateName { name }) if name == b"foo"));
367    }
368}