blob: f2341e3cc0dd2cc6468c95bc4fca966af26eafe1 [file] [log] [blame]
Janis Danisevskis93927dd2020-12-23 12:23:08 -08001// Copyright 2020, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! This module implements the key garbage collector.
16//! The key garbage collector has one public function `notify_gc()`. This will create
17//! a thread on demand which will query the database for unreferenced key entries,
18//! optionally dispose of sensitive key material appropriately, and then delete
19//! the key entry from the database.
20
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +000021use crate::ks_err;
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080022use crate::{
23 async_task,
David Drysdalef1ba3812024-05-08 17:50:13 +010024 database::{KeystoreDB, SupersededBlob, Uuid},
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000025 super_key::SuperKeyManager,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080026};
27use anyhow::{Context, Result};
28use async_task::AsyncTask;
Janis Danisevskis3395f862021-05-06 10:54:17 -070029use std::sync::{
30 atomic::{AtomicU8, Ordering},
Janis Danisevskis0fd25a62022-01-04 19:53:37 -080031 Arc, RwLock,
Janis Danisevskis3395f862021-05-06 10:54:17 -070032};
Janis Danisevskis93927dd2020-12-23 12:23:08 -080033
Janis Danisevskis93927dd2020-12-23 12:23:08 -080034pub struct Gc {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080035 async_task: Arc<AsyncTask>,
Janis Danisevskis3395f862021-05-06 10:54:17 -070036 notified: Arc<AtomicU8>,
Janis Danisevskis93927dd2020-12-23 12:23:08 -080037}
38
39impl Gc {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080040 /// Creates a garbage collector using the given async_task.
Janis Danisevskis3395f862021-05-06 10:54:17 -070041 /// The garbage collector needs a function to invalidate key blobs, a database connection,
42 /// and a reference to the `SuperKeyManager`. They are obtained from the init function.
43 /// The function is only called if this is first time a garbage collector was initialized
44 /// with the given AsyncTask instance.
45 /// Note: It is a logical error to initialize different Gc instances with the same `AsyncTask`.
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080046 pub fn new_init_with<F>(async_task: Arc<AsyncTask>, init: F) -> Self
47 where
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000048 F: FnOnce() -> (
49 Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
50 KeystoreDB,
Janis Danisevskis0fd25a62022-01-04 19:53:37 -080051 Arc<RwLock<SuperKeyManager>>,
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000052 ) + Send
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080053 + 'static,
54 {
55 let weak_at = Arc::downgrade(&async_task);
Janis Danisevskis3395f862021-05-06 10:54:17 -070056 let notified = Arc::new(AtomicU8::new(0));
57 let notified_clone = notified.clone();
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080058 // Initialize the task's shelf.
59 async_task.queue_hi(move |shelf| {
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000060 let (invalidate_key, db, super_key) = init();
Janis Danisevskis3395f862021-05-06 10:54:17 -070061 let notified = notified_clone;
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080062 shelf.get_or_put_with(|| GcInternal {
Janis Danisevskis3395f862021-05-06 10:54:17 -070063 deleted_blob_ids: vec![],
64 superseded_blobs: vec![],
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080065 invalidate_key,
66 db,
67 async_task: weak_at,
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000068 super_key,
Janis Danisevskis3395f862021-05-06 10:54:17 -070069 notified,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080070 });
71 });
Janis Danisevskis3395f862021-05-06 10:54:17 -070072 Self { async_task, notified }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080073 }
Janis Danisevskis93927dd2020-12-23 12:23:08 -080074
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080075 /// Notifies the key garbage collector to iterate through orphaned and superseded blobs and
76 /// attempts their deletion. We only process one key at a time and then schedule another
77 /// attempt by queueing it in the async_task (low priority) queue.
78 pub fn notify_gc(&self) {
Janis Danisevskis3395f862021-05-06 10:54:17 -070079 if let Ok(0) = self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed) {
80 self.async_task.queue_lo(|shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step())
81 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080082 }
83}
84
85struct GcInternal {
Janis Danisevskis3395f862021-05-06 10:54:17 -070086 deleted_blob_ids: Vec<i64>,
David Drysdalef1ba3812024-05-08 17:50:13 +010087 superseded_blobs: Vec<SupersededBlob>,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080088 invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
89 db: KeystoreDB,
90 async_task: std::sync::Weak<AsyncTask>,
Janis Danisevskis0fd25a62022-01-04 19:53:37 -080091 super_key: Arc<RwLock<SuperKeyManager>>,
Janis Danisevskis3395f862021-05-06 10:54:17 -070092 notified: Arc<AtomicU8>,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080093}
94
95impl GcInternal {
96 /// Attempts to process one blob from the database.
Janis Danisevskis93927dd2020-12-23 12:23:08 -080097 /// We process one key at a time, because deleting a key is a time consuming process which
98 /// may involve calling into the KeyMint backend and we don't want to hog neither the backend
99 /// nor the database for extended periods of time.
Janis Danisevskis3395f862021-05-06 10:54:17 -0700100 /// To limit the number of database transactions, which are also expensive and competing
101 /// with threads on the critical path, deleted blobs are loaded in batches.
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800102 fn process_one_key(&mut self) -> Result<()> {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700103 if self.superseded_blobs.is_empty() {
104 let blobs = self
105 .db
106 .handle_next_superseded_blobs(&self.deleted_blob_ids, 20)
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000107 .context(ks_err!("Trying to handle superseded blob."))?;
Janis Danisevskis3395f862021-05-06 10:54:17 -0700108 self.deleted_blob_ids = vec![];
109 self.superseded_blobs = blobs;
110 }
111
David Drysdalef1ba3812024-05-08 17:50:13 +0100112 if let Some(SupersededBlob { blob_id, blob, metadata }) = self.superseded_blobs.pop() {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700113 // Add the next blob_id to the deleted blob ids list. So it will be
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800114 // removed from the database regardless of whether the following
115 // succeeds or not.
Janis Danisevskis3395f862021-05-06 10:54:17 -0700116 self.deleted_blob_ids.push(blob_id);
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800117
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800118 // If the key has a km_uuid we try to get the corresponding device
119 // and delete the key, unwrapping if necessary and possible.
120 // (At this time keys may get deleted without having the super encryption
121 // key in this case we can only delete the key from the database.)
David Drysdalef1ba3812024-05-08 17:50:13 +0100122 if let Some(uuid) = metadata.km_uuid() {
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +0000123 let blob = self
124 .super_key
Janis Danisevskis0fd25a62022-01-04 19:53:37 -0800125 .read()
126 .unwrap()
David Drysdalef1ba3812024-05-08 17:50:13 +0100127 .unwrap_key_if_required(&metadata, &blob)
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000128 .context(ks_err!("Trying to unwrap to-be-deleted blob.",))?;
Chris Wailesdabb6fe2022-11-16 15:56:19 -0800129 (self.invalidate_key)(uuid, &blob).context(ks_err!("Trying to invalidate key."))?;
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800130 }
131 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800132 Ok(())
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800133 }
134
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800135 /// Processes one key and then schedules another attempt until it runs out of blobs to delete.
136 fn step(&mut self) {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700137 self.notified.store(0, Ordering::Relaxed);
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800138 if let Err(e) = self.process_one_key() {
139 log::error!("Error trying to delete blob entry. {:?}", e);
140 }
141 // Schedule the next step. This gives high priority requests a chance to interleave.
Janis Danisevskis3395f862021-05-06 10:54:17 -0700142 if !self.deleted_blob_ids.is_empty() {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800143 if let Some(at) = self.async_task.upgrade() {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700144 if let Ok(0) =
145 self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed)
146 {
147 at.queue_lo(move |shelf| {
148 shelf.get_downcast_mut::<GcInternal>().unwrap().step()
149 });
150 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800151 }
152 }
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800153 }
154}