1use super::concurrent_cache::{LockFreeQueryCache, StorageStrategy};
6use super::{AccessQueryArgs, XpermsAccessQueryArgs};
7use crate::SecurityId;
8use crate::kernel::{ClassPermission, KernelAccessDecision, KernelXpermsAccessDecision};
9use crate::policy::{XpermsBitmap, XpermsKind};
10use std::hash::{Hash, Hasher};
11use std::sync::atomic::{AtomicU8, AtomicU16, AtomicU32, AtomicU64, Ordering};
12use zerocopy::IntoBytes;
13
14pub type ConcurrentAccessCache = LockFreeQueryCache<
17 AccessCacheStorage,
18 1,
19 3,
20 0,
21 0,
22 1,
23 0,
24>;
25
26pub(super) type ConcurrentXpermsCache = LockFreeQueryCache<
30 XpermsAccessCacheStorage,
31 1,
32 1,
33 0,
34 1,
35 1,
36 8,
37>;
38
39pub(super) type ConcurrentSidCache = LockFreeQueryCache<
42 SidCacheStorage,
43 2,
44 1,
45 1,
46 0,
47 1,
48 0,
49>;
50
51#[derive(Default)]
52pub struct AccessCacheStorage;
53
54impl
57 StorageStrategy<
58 3,
59 0,
60 0,
61 1,
62 0,
63 > for AccessCacheStorage
64{
65 type Key = AccessQueryArgs;
66 type Value = KernelAccessDecision;
67
68 #[inline(always)]
69 fn hash_key(&self, key: &Self::Key) -> u64 {
70 rapidhash::rapidhash(key.as_bytes())
71 }
72
73 #[inline(always)]
74 fn check_key(
75 &self,
76 key: &Self::Key,
77 inline_u64s: &[AtomicU64; 3],
78 _inline_u32s: &[AtomicU32; 0],
79 _inline_u16s: &[AtomicU16; 0],
80 inline_u8s: &[AtomicU8; 1],
81 _out_of_line_u64s: &[AtomicU64; 0],
82 ) -> bool {
83 inline_u8s[0].load(Ordering::Relaxed) == key.target_class as u8
84 && inline_u64s[0].load(Ordering::Relaxed)
85 == (key.source_sid.0.get() as u64 | (key.target_sid.0.get() as u64) << 32)
86 }
87
88 #[inline(always)]
89 fn read_value(
90 &self,
91 inline_u64s: &[AtomicU64; 3],
92 _inline_u32s: &[AtomicU32; 0],
93 _inline_u16s: &[AtomicU16; 0],
94 _inline_u8s: &[AtomicU8; 1],
95 _out_of_line_u64s: &[AtomicU64; 0],
96 ) -> Self::Value {
97 let u64_1 = inline_u64s[1].load(Ordering::Relaxed);
98 let u64_2 = inline_u64s[2].load(Ordering::Relaxed);
99
100 let allow_u32 = (u64_1 >> 32) as u32;
101 let audit_u32 = (u64_1 & 0xFFFFFFFF) as u32;
102 let flags = (u64_2 >> 32) as u32;
103 let todo_u64 = u64_2 & 0xFFFFFFFF;
104
105 KernelAccessDecision {
106 allow: allow_u32.into(),
107 audit: audit_u32.into(),
108 flags,
109 todo_bug: if todo_u64 == 0 {
110 None
111 } else {
112 Some(std::num::NonZeroU32::new(todo_u64 as u32).unwrap())
113 },
114 }
115 }
116
117 #[inline(always)]
118 fn write_key_value(
119 &self,
120 key: &Self::Key,
121 value: &Self::Value,
122 inline_u64s: &[AtomicU64; 3],
123 _inline_u32s: &[AtomicU32; 0],
124 _inline_u16s: &[AtomicU16; 0],
125 inline_u8s: &[AtomicU8; 1],
126 _out_of_line_u64s: &[AtomicU64; 0],
127 ) {
128 let source_sid = key.source_sid.0.get() as u64;
129 let target_sid = key.target_sid.0.get() as u64;
130 let target_class = key.target_class.clone() as u8;
131
132 let allow_u32: u32 = value.allow.into();
133 let allow = allow_u32 as u64;
134 let audit_u32: u32 = value.audit.into();
135 let audit = audit_u32 as u64;
136 let flags = value.flags as u64;
137 let todo_bug = match value.todo_bug {
138 Some(n) => n.get() as u64,
139 None => 0,
140 };
141
142 let u64_0 = source_sid | (target_sid << 32);
143 let u64_1 = audit | (allow << 32);
144 let u64_2 = todo_bug | (flags << 32);
145
146 inline_u64s[0].store(u64_0, Ordering::Relaxed);
147 inline_u64s[1].store(u64_1, Ordering::Relaxed);
148 inline_u64s[2].store(u64_2, Ordering::Relaxed);
149 inline_u8s[0].store(target_class, Ordering::Relaxed);
150 }
151}
152
153#[derive(Default)]
154pub(super) struct XpermsAccessCacheStorage;
155
156impl XpermsAccessCacheStorage {
157 const PERMISSION_ID_MASK: u8 = 0b0001_1111;
158 const XPERMS_KIND_BIT_INDEX: usize = 5;
159 const PERMISSIVE_BIT_INDEX: usize = 6;
160 const HAS_TODO_BIT_INDEX: usize = 7;
161}
162
163impl
167 StorageStrategy<
168 1,
169 0,
170 1,
171 1,
172 8,
173 > for XpermsAccessCacheStorage
174{
175 type Key = XpermsAccessQueryArgs;
176 type Value = KernelXpermsAccessDecision;
177
178 #[inline(always)]
179 fn hash_key(&self, key: &Self::Key) -> u64 {
180 let mut hasher = rapidhash::RapidInlineHasher::default();
181 key.hash(&mut hasher);
182 hasher.finish()
183 }
184
185 #[inline(always)]
186 fn check_key(
187 &self,
188 key: &Self::Key,
189 inline_u64s: &[AtomicU64; 1],
190 _inline_u32s: &[AtomicU32; 0],
191 inline_u16s: &[AtomicU16; 1],
192 inline_u8s: &[AtomicU8; 1],
193 _out_of_line_u64s: &[AtomicU64; 8],
194 ) -> bool {
195 let source_sid = key.source_sid.0.get() as u64;
196 let target_sid = key.target_sid.0.get() as u64;
197 let class = key.permission.class() as u16;
198 let xperms_prefix = key.xperms_prefix as u16;
199 let permission_id = key.permission.id() as u8;
200 let xperms_kind_bit = match key.xperms_kind {
201 XpermsKind::Ioctl => 0,
202 XpermsKind::Nlmsg => 1,
203 };
204
205 let u64_0_matches =
206 inline_u64s[0].load(Ordering::Relaxed) == (source_sid | (target_sid << 32));
207 let u16_0_matches =
208 inline_u16s[0].load(Ordering::Relaxed) == (class | (xperms_prefix << 8));
209 let u8_0_val = inline_u8s[0].load(Ordering::Relaxed);
210 let u8_0_matches = (u8_0_val
211 & (Self::PERMISSION_ID_MASK | (1u8 << Self::XPERMS_KIND_BIT_INDEX)))
212 == (permission_id | (xperms_kind_bit << Self::XPERMS_KIND_BIT_INDEX));
213
214 u64_0_matches && u16_0_matches && u8_0_matches
215 }
216
217 #[inline(always)]
218 fn read_value(
219 &self,
220 _inline_u64s: &[AtomicU64; 1],
221 _inline_u32s: &[AtomicU32; 0],
222 _inline_u16s: &[AtomicU16; 1],
223 inline_u8s: &[AtomicU8; 1],
224 out_of_line_u64s: &[AtomicU64; 8],
225 ) -> Self::Value {
226 let u8_0 = inline_u8s[0].load(Ordering::Relaxed);
227 let permissive = (u8_0 & (1u8 << Self::PERMISSIVE_BIT_INDEX)) != 0;
228 let has_todo = (u8_0 & (1u8 << Self::HAS_TODO_BIT_INDEX)) != 0;
229
230 let (chunks, _) = out_of_line_u64s.as_chunks::<{ XpermsBitmap::BITMAP_BLOCKS }>();
231 let allow = XpermsBitmap::from_atomics(&chunks[0]);
232 let audit = XpermsBitmap::from_atomics(&chunks[1]);
233
234 KernelXpermsAccessDecision { allow, audit, permissive, has_todo }
235 }
236
237 #[inline(always)]
238 fn write_key_value(
239 &self,
240 key: &Self::Key,
241 value: &Self::Value,
242 inline_u64s: &[AtomicU64; 1],
243 _inline_u32s: &[AtomicU32; 0],
244 inline_u16s: &[AtomicU16; 1],
245 inline_u8s: &[AtomicU8; 1],
246 out_of_line_u64s: &[AtomicU64; 8],
247 ) {
248 let source_sid = key.source_sid.0.get() as u64;
249 let target_sid = key.target_sid.0.get() as u64;
250 let class = key.permission.class() as u16;
251 let xperms_prefix = key.xperms_prefix as u16;
252 let permission_id = key.permission.id() as u8;
253 let xperms_kind_bit = match key.xperms_kind {
254 XpermsKind::Ioctl => 0,
255 XpermsKind::Nlmsg => 1,
256 };
257
258 let u64_0 = source_sid | (target_sid << 32);
259 let u16_0 = class | (xperms_prefix << 8);
260 let u8_0 = permission_id
261 | (xperms_kind_bit << Self::XPERMS_KIND_BIT_INDEX)
262 | ((value.permissive as u8) << Self::PERMISSIVE_BIT_INDEX)
263 | ((value.has_todo as u8) << Self::HAS_TODO_BIT_INDEX);
264
265 inline_u64s[0].store(u64_0, Ordering::Relaxed);
266 inline_u16s[0].store(u16_0, Ordering::Relaxed);
267 inline_u8s[0].store(u8_0, Ordering::Relaxed);
268
269 let (chunks, _) = out_of_line_u64s.as_chunks::<{ XpermsBitmap::BITMAP_BLOCKS }>();
270 value.allow.to_atomics(&chunks[0]);
271 value.audit.to_atomics(&chunks[1]);
272 }
273}
274
275#[derive(Default)]
276pub(super) struct SidCacheStorage;
277
278impl
281 StorageStrategy<
282 1,
283 1,
284 0,
285 1,
286 0,
287 > for SidCacheStorage
288{
289 type Key = AccessQueryArgs;
290 type Value = SecurityId;
291
292 #[inline(always)]
293 fn hash_key(&self, key: &Self::Key) -> u64 {
294 rapidhash::rapidhash(key.as_bytes())
295 }
296
297 #[inline(always)]
298 fn check_key(
299 &self,
300 key: &Self::Key,
301 inline_u64s: &[AtomicU64; 1],
302 _inline_u32s: &[AtomicU32; 1],
303 _inline_u16s: &[AtomicU16; 0],
304 inline_u8s: &[AtomicU8; 1],
305 _out_of_line_u64s: &[AtomicU64; 0],
306 ) -> bool {
307 inline_u8s[0].load(Ordering::Relaxed) == key.target_class as u8
308 && inline_u64s[0].load(Ordering::Relaxed)
309 == (key.source_sid.0.get() as u64 | (key.target_sid.0.get() as u64) << 32)
310 }
311
312 #[inline(always)]
313 fn read_value(
314 &self,
315 _inline_u64s: &[AtomicU64; 1],
316 inline_u32s: &[AtomicU32; 1],
317 _inline_u16s: &[AtomicU16; 0],
318 _inline_u8s: &[AtomicU8; 1],
319 _out_of_line_u64s: &[AtomicU64; 0],
320 ) -> Self::Value {
321 let u32_val = inline_u32s[0].load(Ordering::Relaxed);
322 SecurityId(std::num::NonZeroU32::new(u32_val).unwrap_or(std::num::NonZeroU32::MIN))
323 }
324
325 #[inline(always)]
326 fn write_key_value(
327 &self,
328 key: &Self::Key,
329 value: &Self::Value,
330 inline_u64s: &[AtomicU64; 1],
331 inline_u32s: &[AtomicU32; 1],
332 _inline_u16s: &[AtomicU16; 0],
333 inline_u8s: &[AtomicU8; 1],
334 _out_of_line_u64s: &[AtomicU64; 0],
335 ) {
336 let source_sid = key.source_sid.0.get() as u64;
337 let target_sid = key.target_sid.0.get() as u64;
338 let target_class = key.target_class.clone() as u8;
339 let value_sid = value.0.get() as u32;
340
341 let u64_0 = source_sid | (target_sid << 32);
342
343 inline_u64s[0].store(u64_0, Ordering::Relaxed);
344 inline_u32s[0].store(value_sid, Ordering::Relaxed);
345 inline_u8s[0].store(target_class, Ordering::Relaxed);
346 }
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352 use crate::kernel::{DirPermission, KernelClass, KernelPermission};
353 use crate::policy::{AccessVector, XpermsBitmap};
354
355 #[test]
356 fn test_access_cache_storage_roundtrip() {
357 let key = AccessQueryArgs {
358 source_sid: SecurityId(1.try_into().unwrap()),
359 target_sid: SecurityId(2.try_into().unwrap()),
360 target_class: KernelClass::File,
361 };
362 let value = KernelAccessDecision {
363 allow: AccessVector::from(4),
364 audit: AccessVector::from(5),
365 flags: 42,
366 todo_bug: Some(12345.try_into().unwrap()),
367 };
368
369 let inline_u64s = std::array::from_fn(|_| AtomicU64::new(0));
370 let inline_u32s = std::array::from_fn(|_| AtomicU32::new(0));
371 let inline_u16s = std::array::from_fn(|_| AtomicU16::new(0));
372 let inline_u8s = std::array::from_fn(|_| AtomicU8::new(0));
373 let out_of_line_u64s = std::array::from_fn(|_| AtomicU64::new(0));
374
375 AccessCacheStorage::default().write_key_value(
376 &key,
377 &value,
378 &inline_u64s,
379 &inline_u32s,
380 &inline_u16s,
381 &inline_u8s,
382 &out_of_line_u64s,
383 );
384
385 assert!(AccessCacheStorage::default().check_key(
386 &key,
387 &inline_u64s,
388 &inline_u32s,
389 &inline_u16s,
390 &inline_u8s,
391 &out_of_line_u64s,
392 ));
393
394 let read_val = AccessCacheStorage::default().read_value(
395 &inline_u64s,
396 &inline_u32s,
397 &inline_u16s,
398 &inline_u8s,
399 &out_of_line_u64s,
400 );
401
402 assert_eq!(read_val, value);
403 }
404
405 #[test]
406 fn test_xperms_access_cache_storage_roundtrip() {
407 let key = XpermsAccessQueryArgs {
408 xperms_kind: XpermsKind::Ioctl,
409 source_sid: SecurityId(1.try_into().unwrap()),
410 target_sid: SecurityId(2.try_into().unwrap()),
411 permission: KernelPermission::Dir(DirPermission::AddName),
412 xperms_prefix: 0,
413 };
414 let value = KernelXpermsAccessDecision {
415 allow: XpermsBitmap::NONE,
416 audit: XpermsBitmap::NONE,
417 permissive: false,
418 has_todo: true,
419 };
420
421 let inline_u64s = std::array::from_fn(|_| AtomicU64::new(0));
422 let inline_u32s = std::array::from_fn(|_| AtomicU32::new(0));
423 let inline_u16s = std::array::from_fn(|_| AtomicU16::new(0));
424 let inline_u8s = std::array::from_fn(|_| AtomicU8::new(0));
425 let out_of_line_u64s = std::array::from_fn(|_| AtomicU64::new(0));
426
427 XpermsAccessCacheStorage::default().write_key_value(
428 &key,
429 &value,
430 &inline_u64s,
431 &inline_u32s,
432 &inline_u16s,
433 &inline_u8s,
434 &out_of_line_u64s,
435 );
436
437 assert!(XpermsAccessCacheStorage::default().check_key(
438 &key,
439 &inline_u64s,
440 &inline_u32s,
441 &inline_u16s,
442 &inline_u8s,
443 &out_of_line_u64s,
444 ));
445
446 let read_val = XpermsAccessCacheStorage::default().read_value(
447 &inline_u64s,
448 &[],
449 &inline_u16s,
450 &inline_u8s,
451 &out_of_line_u64s,
452 );
453
454 assert_eq!(read_val, value);
455 }
456
457 #[test]
458 fn test_sid_cache_storage_roundtrip() {
459 let key = AccessQueryArgs {
460 source_sid: SecurityId(1.try_into().unwrap()),
461 target_sid: SecurityId(2.try_into().unwrap()),
462 target_class: KernelClass::Process,
463 };
464 let value = SecurityId(3.try_into().unwrap());
465
466 let inline_u64s = std::array::from_fn(|_| AtomicU64::new(0));
467 let inline_u32s = std::array::from_fn(|_| AtomicU32::new(0));
468 let inline_u16s = std::array::from_fn(|_| AtomicU16::new(0));
469 let inline_u8s = std::array::from_fn(|_| AtomicU8::new(0));
470 let out_of_line_u64s = std::array::from_fn(|_| AtomicU64::new(0));
471
472 SidCacheStorage::default().write_key_value(
473 &key,
474 &value,
475 &inline_u64s,
476 &inline_u32s,
477 &inline_u16s,
478 &inline_u8s,
479 &out_of_line_u64s,
480 );
481
482 assert!(SidCacheStorage::default().check_key(
483 &key,
484 &inline_u64s,
485 &inline_u32s,
486 &inline_u16s,
487 &inline_u8s,
488 &out_of_line_u64s,
489 ));
490
491 let read_val = SidCacheStorage::default().read_value(
492 &inline_u64s,
493 &inline_u32s,
494 &inline_u16s,
495 &inline_u8s,
496 &out_of_line_u64s,
497 );
498
499 assert_eq!(read_val, value);
500 }
501
502 #[test]
503 fn test_access_cache_bucket_size() {
504 assert_eq!(ConcurrentAccessCache::bucket_size(), 128);
506 }
507
508 #[test]
509 fn test_xperms_cache_bucket_size() {
510 assert_eq!(ConcurrentXpermsCache::bucket_size(), 64);
512 }
513
514 #[test]
515 fn test_sid_cache_bucket_size() {
516 assert_eq!(ConcurrentSidCache::bucket_size(), 128);
518 }
519}