diff --git a/drivers/staging/android/ashmem_rust.rs b/drivers/staging/android/ashmem_rust.rs index fb50a5aceb9e..ae96ff09a0b5 100644 --- a/drivers/staging/android/ashmem_rust.rs +++ b/drivers/staging/android/ashmem_rust.rs @@ -45,7 +45,7 @@ mod ashmem_shrinker; use ashmem_shrinker::{ShrinkerBuilder, ShrinkerRegistration}; mod ashmem_range; -use ashmem_range::{Area, AshmemGuard, NewRange, ASHMEM_MUTEX}; +use ashmem_range::{Area, AshmemGuard, NewRange, ASHMEM_MUTEX, LRU_COUNT}; mod shmem; use shmem::ShmemFile; @@ -57,6 +57,12 @@ fn read_implies_exec(task: &Task) -> bool { (personality & bindings::READ_IMPLIES_EXEC) != 0 } +/// Calls `capable(CAP_SYS_ADMIN)`. +fn has_cap_sys_admin() -> bool { + use kernel::bindings::CAP_SYS_ADMIN; + unsafe { bindings::capable(CAP_SYS_ADMIN as c_int) } +} + static NUM_PIN_IOCTLS_WAITING: AtomicUsize = AtomicUsize::new(0); fn shrinker_should_stop() -> bool { @@ -239,6 +245,7 @@ impl MiscDevice for Ashmem { ASHMEM_PIN | ASHMEM_UNPIN | ASHMEM_GET_PIN_STATUS => { me.pin_unpin(cmd, UserSlice::new(arg, size).reader()) } + bindings::ASHMEM_PURGE_ALL_CACHES => me.purge_all_caches(), _ => Err(EINVAL), } } @@ -430,6 +437,17 @@ impl Ashmem { _ => unreachable!(), } } + + fn purge_all_caches(&self) -> Result { + if !has_cap_sys_admin() { + return Err(EPERM); + } + let mut guard = AshmemGuard(ASHMEM_MUTEX.lock()); + let total_num_pages = LRU_COUNT.load(Ordering::Relaxed); + let _num_freed = guard.free_lru(usize::MAX); + // ASHMEM_PURGE_ALL_CACHES returns the total number of pages even if we stopped early. + Ok(isize::try_from(total_num_pages).unwrap_or(isize::MAX)) + } } impl AshmemInner {