blob: 2010c796a98f407d35d4c62aeb008f5b0a038195 [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
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080021use crate::{
22 async_task,
Janis Danisevskis3395f862021-05-06 10:54:17 -070023 database::{BlobMetaData, KeystoreDB, Uuid},
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000024 super_key::SuperKeyManager,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080025};
26use anyhow::{Context, Result};
27use async_task::AsyncTask;
Janis Danisevskis3395f862021-05-06 10:54:17 -070028use std::sync::{
29 atomic::{AtomicU8, Ordering},
30 Arc,
31};
Janis Danisevskis93927dd2020-12-23 12:23:08 -080032
Janis Danisevskis93927dd2020-12-23 12:23:08 -080033pub struct Gc {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080034 async_task: Arc<AsyncTask>,
Janis Danisevskis3395f862021-05-06 10:54:17 -070035 notified: Arc<AtomicU8>,
Janis Danisevskis93927dd2020-12-23 12:23:08 -080036}
37
38impl Gc {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080039 /// Creates a garbage collector using the given async_task.
Janis Danisevskis3395f862021-05-06 10:54:17 -070040 /// The garbage collector needs a function to invalidate key blobs, a database connection,
41 /// and a reference to the `SuperKeyManager`. They are obtained from the init function.
42 /// The function is only called if this is first time a garbage collector was initialized
43 /// with the given AsyncTask instance.
44 /// Note: It is a logical error to initialize different Gc instances with the same `AsyncTask`.
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080045 pub fn new_init_with<F>(async_task: Arc<AsyncTask>, init: F) -> Self
46 where
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000047 F: FnOnce() -> (
48 Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
49 KeystoreDB,
50 Arc<SuperKeyManager>,
51 ) + Send
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080052 + 'static,
53 {
54 let weak_at = Arc::downgrade(&async_task);
Janis Danisevskis3395f862021-05-06 10:54:17 -070055 let notified = Arc::new(AtomicU8::new(0));
56 let notified_clone = notified.clone();
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080057 // Initialize the task's shelf.
58 async_task.queue_hi(move |shelf| {
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000059 let (invalidate_key, db, super_key) = init();
Janis Danisevskis3395f862021-05-06 10:54:17 -070060 let notified = notified_clone;
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080061 shelf.get_or_put_with(|| GcInternal {
Janis Danisevskis3395f862021-05-06 10:54:17 -070062 deleted_blob_ids: vec![],
63 superseded_blobs: vec![],
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080064 invalidate_key,
65 db,
66 async_task: weak_at,
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000067 super_key,
Janis Danisevskis3395f862021-05-06 10:54:17 -070068 notified,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080069 });
70 });
Janis Danisevskis3395f862021-05-06 10:54:17 -070071 Self { async_task, notified }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080072 }
Janis Danisevskis93927dd2020-12-23 12:23:08 -080073
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080074 /// Notifies the key garbage collector to iterate through orphaned and superseded blobs and
75 /// attempts their deletion. We only process one key at a time and then schedule another
76 /// attempt by queueing it in the async_task (low priority) queue.
77 pub fn notify_gc(&self) {
Janis Danisevskis3395f862021-05-06 10:54:17 -070078 if let Ok(0) = self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed) {
79 self.async_task.queue_lo(|shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step())
80 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080081 }
82}
83
84struct GcInternal {
Janis Danisevskis3395f862021-05-06 10:54:17 -070085 deleted_blob_ids: Vec<i64>,
86 superseded_blobs: Vec<(i64, Vec<u8>, BlobMetaData)>,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080087 invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
88 db: KeystoreDB,
89 async_task: std::sync::Weak<AsyncTask>,
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000090 super_key: Arc<SuperKeyManager>,
Janis Danisevskis3395f862021-05-06 10:54:17 -070091 notified: Arc<AtomicU8>,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080092}
93
94impl GcInternal {
95 /// Attempts to process one blob from the database.
Janis Danisevskis93927dd2020-12-23 12:23:08 -080096 /// We process one key at a time, because deleting a key is a time consuming process which
97 /// may involve calling into the KeyMint backend and we don't want to hog neither the backend
98 /// nor the database for extended periods of time.
Janis Danisevskis3395f862021-05-06 10:54:17 -070099 /// To limit the number of database transactions, which are also expensive and competing
100 /// with threads on the critical path, deleted blobs are loaded in batches.
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800101 fn process_one_key(&mut self) -> Result<()> {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700102 if self.superseded_blobs.is_empty() {
103 let blobs = self
104 .db
105 .handle_next_superseded_blobs(&self.deleted_blob_ids, 20)
106 .context("In process_one_key: Trying to handle superseded blob.")?;
107 self.deleted_blob_ids = vec![];
108 self.superseded_blobs = blobs;
109 }
110
111 if let Some((blob_id, blob, blob_metadata)) = self.superseded_blobs.pop() {
112 // Add the next blob_id to the deleted blob ids list. So it will be
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800113 // removed from the database regardless of whether the following
114 // succeeds or not.
Janis Danisevskis3395f862021-05-06 10:54:17 -0700115 self.deleted_blob_ids.push(blob_id);
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800116
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800117 // If the key has a km_uuid we try to get the corresponding device
118 // and delete the key, unwrapping if necessary and possible.
119 // (At this time keys may get deleted without having the super encryption
120 // key in this case we can only delete the key from the database.)
121 if let Some(uuid) = blob_metadata.km_uuid() {
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +0000122 let blob = self
123 .super_key
124 .unwrap_key_if_required(&blob_metadata, &blob)
125 .context("In process_one_key: Trying to unwrap to-be-deleted blob.")?;
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800126 (self.invalidate_key)(&uuid, &*blob)
127 .context("In process_one_key: Trying to invalidate key.")?;
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800128 }
129 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800130 Ok(())
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800131 }
132
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800133 /// Processes one key and then schedules another attempt until it runs out of blobs to delete.
134 fn step(&mut self) {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700135 self.notified.store(0, Ordering::Relaxed);
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800136 if let Err(e) = self.process_one_key() {
137 log::error!("Error trying to delete blob entry. {:?}", e);
138 }
139 // Schedule the next step. This gives high priority requests a chance to interleave.
Janis Danisevskis3395f862021-05-06 10:54:17 -0700140 if !self.deleted_blob_ids.is_empty() {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800141 if let Some(at) = self.async_task.upgrade() {
Janis Danisevskis3395f862021-05-06 10:54:17 -0700142 if let Ok(0) =
143 self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed)
144 {
145 at.queue_lo(move |shelf| {
146 shelf.get_downcast_mut::<GcInternal>().unwrap().step()
147 });
148 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800149 }
150 }
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800151 }
152}