blob: 974167180d32bd018484b5fe5a2556576cf1d440 [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},
David Drysdale940a0ff2025-01-13 14:19:16 +000025 globals,
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000026 super_key::SuperKeyManager,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080027};
28use anyhow::{Context, Result};
29use async_task::AsyncTask;
Janis Danisevskis3395f862021-05-06 10:54:17 -070030use std::sync::{
31 atomic::{AtomicU8, Ordering},
Janis Danisevskis0fd25a62022-01-04 19:53:37 -080032 Arc, RwLock,
Janis Danisevskis3395f862021-05-06 10:54:17 -070033};
Janis Danisevskis93927dd2020-12-23 12:23:08 -080034
Janis Danisevskis93927dd2020-12-23 12:23:08 -080035pub struct Gc {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080036 async_task: Arc<AsyncTask>,
Janis Danisevskis3395f862021-05-06 10:54:17 -070037 notified: Arc<AtomicU8>,
Janis Danisevskis93927dd2020-12-23 12:23:08 -080038}
39
40impl Gc {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080041 /// Creates a garbage collector using the given async_task.
Janis Danisevskis3395f862021-05-06 10:54:17 -070042 /// The garbage collector needs a function to invalidate key blobs, a database connection,
43 /// and a reference to the `SuperKeyManager`. They are obtained from the init function.
44 /// The function is only called if this is first time a garbage collector was initialized
45 /// with the given AsyncTask instance.
46 /// Note: It is a logical error to initialize different Gc instances with the same `AsyncTask`.
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080047 pub fn new_init_with<F>(async_task: Arc<AsyncTask>, init: F) -> Self
48 where
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000049 F: FnOnce() -> (
50 Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
51 KeystoreDB,
Janis Danisevskis0fd25a62022-01-04 19:53:37 -080052 Arc<RwLock<SuperKeyManager>>,
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000053 ) + Send
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080054 + 'static,
55 {
56 let weak_at = Arc::downgrade(&async_task);
Janis Danisevskis3395f862021-05-06 10:54:17 -070057 let notified = Arc::new(AtomicU8::new(0));
58 let notified_clone = notified.clone();
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080059 // Initialize the task's shelf.
60 async_task.queue_hi(move |shelf| {
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000061 let (invalidate_key, db, super_key) = init();
Janis Danisevskis3395f862021-05-06 10:54:17 -070062 let notified = notified_clone;
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080063 shelf.get_or_put_with(|| GcInternal {
Janis Danisevskis3395f862021-05-06 10:54:17 -070064 deleted_blob_ids: vec![],
65 superseded_blobs: vec![],
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080066 invalidate_key,
67 db,
68 async_task: weak_at,
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000069 super_key,
Janis Danisevskis3395f862021-05-06 10:54:17 -070070 notified,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080071 });
72 });
Janis Danisevskis3395f862021-05-06 10:54:17 -070073 Self { async_task, notified }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080074 }
Janis Danisevskis93927dd2020-12-23 12:23:08 -080075
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080076 /// Notifies the key garbage collector to iterate through orphaned and superseded blobs and
77 /// attempts their deletion. We only process one key at a time and then schedule another
78 /// attempt by queueing it in the async_task (low priority) queue.
79 pub fn notify_gc(&self) {
Janis Danisevskis3395f862021-05-06 10:54:17 -070080 if let Ok(0) = self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed) {
81 self.async_task.queue_lo(|shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step())
82 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080083 }
84}
85
86struct GcInternal {
Janis Danisevskis3395f862021-05-06 10:54:17 -070087 deleted_blob_ids: Vec<i64>,
David Drysdalef1ba3812024-05-08 17:50:13 +010088 superseded_blobs: Vec<SupersededBlob>,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080089 invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
90 db: KeystoreDB,
91 async_task: std::sync::Weak<AsyncTask>,
Janis Danisevskis0fd25a62022-01-04 19:53:37 -080092 super_key: Arc<RwLock<SuperKeyManager>>,
Janis Danisevskis3395f862021-05-06 10:54:17 -070093 notified: Arc<AtomicU8>,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080094}
95
96impl GcInternal {
97 /// Attempts to process one blob from the database.
Janis Danisevskis93927dd2020-12-23 12:23:08 -080098 /// We process one key at a time, because deleting a key is a time consuming process which
99 /// may involve calling into the KeyMint backend and we don't want to hog neither the backend
100 /// nor the database for extended periods of time.
Janis Danisevskis3395f862021-05-06 10:54:17 -0700101 /// To limit the number of database transactions, which are also expensive and competing
102 /// with threads on the critical path, deleted blobs are loaded in batches.
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800103 fn process_one_key(&mut self) -> Result<()> {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700104 if self.superseded_blobs.is_empty() {
105 let blobs = self
106 .db
107 .handle_next_superseded_blobs(&self.deleted_blob_ids, 20)
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000108 .context(ks_err!("Trying to handle superseded blob."))?;
Janis Danisevskis3395f862021-05-06 10:54:17 -0700109 self.deleted_blob_ids = vec![];
110 self.superseded_blobs = blobs;
111 }
112
David Drysdalef1ba3812024-05-08 17:50:13 +0100113 if let Some(SupersededBlob { blob_id, blob, metadata }) = self.superseded_blobs.pop() {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700114 // Add the next blob_id to the deleted blob ids list. So it will be
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800115 // removed from the database regardless of whether the following
116 // succeeds or not.
Janis Danisevskis3395f862021-05-06 10:54:17 -0700117 self.deleted_blob_ids.push(blob_id);
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800118
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800119 // If the key has a km_uuid we try to get the corresponding device
120 // and delete the key, unwrapping if necessary and possible.
121 // (At this time keys may get deleted without having the super encryption
122 // key in this case we can only delete the key from the database.)
David Drysdalef1ba3812024-05-08 17:50:13 +0100123 if let Some(uuid) = metadata.km_uuid() {
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +0000124 let blob = self
125 .super_key
Janis Danisevskis0fd25a62022-01-04 19:53:37 -0800126 .read()
127 .unwrap()
David Drysdalef1ba3812024-05-08 17:50:13 +0100128 .unwrap_key_if_required(&metadata, &blob)
Shaquille Johnson9da2e1c2022-09-19 12:39:01 +0000129 .context(ks_err!("Trying to unwrap to-be-deleted blob.",))?;
Chris Wailesdabb6fe2022-11-16 15:56:19 -0800130 (self.invalidate_key)(uuid, &blob).context(ks_err!("Trying to invalidate key."))?;
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800131 }
132 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800133 Ok(())
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800134 }
135
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800136 /// Processes one key and then schedules another attempt until it runs out of blobs to delete.
137 fn step(&mut self) {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700138 self.notified.store(0, Ordering::Relaxed);
David Drysdale940a0ff2025-01-13 14:19:16 +0000139 if !globals::boot_completed() {
140 // Garbage collection involves a operation (`IKeyMintDevice::deleteKey()`) that cannot
141 // be rolled back in some cases (specifically, when the key is rollback-resistant), even
142 // if the Keystore database is restored to the version of an earlier userdata filesystem
143 // checkpoint.
144 //
145 // This means that we should not perform GC until boot has fully completed, and any
146 // in-progress OTA is definitely not going to be rolled back.
147 log::info!("skip GC as boot not completed");
148 return;
149 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800150 if let Err(e) = self.process_one_key() {
151 log::error!("Error trying to delete blob entry. {:?}", e);
152 }
153 // Schedule the next step. This gives high priority requests a chance to interleave.
Janis Danisevskis3395f862021-05-06 10:54:17 -0700154 if !self.deleted_blob_ids.is_empty() {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800155 if let Some(at) = self.async_task.upgrade() {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700156 if let Ok(0) =
157 self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed)
158 {
159 at.queue_lo(move |shelf| {
160 shelf.get_downcast_mut::<GcInternal>().unwrap().step()
161 });
162 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800163 }
164 }
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800165 }
166}