xref: /linux/drivers/android/binder/freeze.rs (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 // Copyright (C) 2025 Google LLC.
4 
5 use kernel::{
6     alloc::AllocError,
7     list::ListArc,
8     prelude::*,
9     rbtree::{self, RBTreeNodeReservation},
10     seq_file::SeqFile,
11     seq_print,
12     sync::{Arc, UniqueArc},
13     uaccess::UserSliceReader,
14 };
15 
16 use crate::{
17     defs::*, node::Node, process::Process, thread::Thread, BinderReturnWriter, DArc, DLArc,
18     DTRWrap, DeliverToRead,
19 };
20 
21 #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
22 pub(crate) struct FreezeCookie(u64);
23 
24 /// Represents a listener for changes to the frozen state of a process.
25 pub(crate) struct FreezeListener {
26     /// The node we are listening for.
27     pub(crate) node: DArc<Node>,
28     /// The cookie of this freeze listener.
29     cookie: FreezeCookie,
30     /// What value of `is_frozen` did we most recently tell userspace about?
31     last_is_frozen: Option<bool>,
32     /// We sent a `BR_FROZEN_BINDER` and we are waiting for `BC_FREEZE_NOTIFICATION_DONE` before
33     /// sending any other commands.
34     is_pending: bool,
35     /// Userspace sent `BC_CLEAR_FREEZE_NOTIFICATION` and we need to reply with
36     /// `BR_CLEAR_FREEZE_NOTIFICATION_DONE` as soon as possible. If `is_pending` is set, then we
37     /// must wait for it to be unset before we can reply.
38     is_clearing: bool,
39     /// Number of cleared duplicates that can't be deleted until userspace sends
40     /// `BC_FREEZE_NOTIFICATION_DONE`.
41     num_pending_duplicates: u64,
42     /// Number of cleared duplicates that can be deleted.
43     num_cleared_duplicates: u64,
44 }
45 
46 impl FreezeListener {
47     /// Is it okay to create a new listener with the same cookie as this one for the provided node?
48     ///
49     /// Under some scenarios, userspace may delete a freeze listener and immediately recreate it
50     /// with the same cookie. This results in duplicate listeners. To avoid issues with ambiguity,
51     /// we allow this only if the new listener is for the same node, and we also require that the
52     /// old listener has already been cleared.
53     fn allow_duplicate(&self, node: &DArc<Node>) -> bool {
54         Arc::ptr_eq(&self.node, node) && self.is_clearing
55     }
56 }
57 
58 type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>;
59 
60 /// Represents a notification that the freeze state has changed.
61 pub(crate) struct FreezeMessage {
62     cookie: FreezeCookie,
63     pid: i32,
64 }
65 
66 kernel::list::impl_list_arc_safe! {
67     impl ListArcSafe<0> for FreezeMessage {
68         untracked;
69     }
70 }
71 
72 impl FreezeMessage {
73     fn new(flags: kernel::alloc::Flags) -> Result<UninitFM, AllocError> {
74         UniqueArc::new_uninit(flags)
75     }
76 
77     fn init(ua: UninitFM, cookie: FreezeCookie, pid: i32) -> DLArc<FreezeMessage> {
78         match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie, pid })) {
79             Ok(msg) => ListArc::from(msg),
80             Err(err) => match err {},
81         }
82     }
83 }
84 
85 impl DeliverToRead for FreezeMessage {
86     fn do_work(
87         self: DArc<Self>,
88         thread: &Thread,
89         writer: &mut BinderReturnWriter<'_>,
90     ) -> Result<bool> {
91         let _removed_listener;
92         let mut node_refs = thread.process.node_refs.lock();
93         let Some(mut freeze_entry) = node_refs.freeze_listeners.find_mut(&self.cookie) else {
94             return Ok(true);
95         };
96         let freeze = freeze_entry.get_mut();
97 
98         if freeze.num_cleared_duplicates > 0 {
99             freeze.num_cleared_duplicates -= 1;
100             drop(node_refs);
101             writer.write_code(BR_CLEAR_FREEZE_NOTIFICATION_DONE)?;
102             writer.write_payload(&self.cookie.0)?;
103             return Ok(true);
104         }
105 
106         if freeze.is_pending {
107             return Ok(true);
108         }
109         if freeze.is_clearing {
110             kernel::warn_on!(freeze.num_cleared_duplicates != 0);
111             if freeze.num_pending_duplicates > 0 {
112                 // The primary freeze listener was deleted, so convert a pending duplicate back
113                 // into the primary one.
114                 freeze.num_pending_duplicates -= 1;
115                 freeze.is_pending = true;
116                 freeze.is_clearing = true;
117             } else {
118                 _removed_listener = freeze_entry.remove_node();
119             }
120             drop(node_refs);
121             writer.write_code(BR_CLEAR_FREEZE_NOTIFICATION_DONE)?;
122             writer.write_payload(&self.cookie.0)?;
123             Ok(true)
124         } else {
125             let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
126             if freeze.last_is_frozen == Some(is_frozen) {
127                 return Ok(true);
128             }
129 
130             let mut state_info = BinderFrozenStateInfo::default();
131             state_info.is_frozen = u32::from(is_frozen);
132             state_info.cookie = freeze.cookie.0;
133             freeze.is_pending = true;
134             freeze.last_is_frozen = Some(is_frozen);
135             drop(node_refs);
136 
137             writer.write_code(BR_FROZEN_BINDER)?;
138             writer.write_payload(&state_info)?;
139             // BR_FROZEN_BINDER notifications can cause transactions
140             Ok(false)
141         }
142     }
143 
144     fn cancel(self: DArc<Self>) {
145         binder_debug!(
146             pid = self.pid,
147             DeadTransaction,
148             "undelivered freeze notification, {:016x}",
149             self.cookie.0
150         );
151     }
152 
153     fn should_sync_wakeup(&self) -> bool {
154         false
155     }
156 
157     #[inline(never)]
158     fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
159         seq_print!(m, "{}has frozen binder\n", prefix);
160         Ok(())
161     }
162 }
163 
164 impl FreezeListener {
165     /// Called when this freeze listener is cleared abnormally.
166     ///
167     /// This occurs either because the process exited or because the process dropped its last
168     /// refcount on the node ref without explicitly removing the freeze listener first.
169     ///
170     /// The returned `KVVec` is just a value that should be dropped outside of the lock.
171     pub(crate) fn on_process_cleanup(&self, proc: &Process) -> KVVec<Arc<Process>> {
172         if !self.is_clearing {
173             return self.node.remove_freeze_listener(proc);
174         }
175         KVVec::new()
176     }
177 }
178 
179 impl Process {
180     pub(crate) fn request_freeze_notif(
181         self: &Arc<Self>,
182         reader: &mut UserSliceReader,
183     ) -> Result<()> {
184         let hc = reader.read::<BinderHandleCookie>()?;
185         let handle = hc.handle;
186         let cookie = FreezeCookie(hc.cookie);
187 
188         let msg = FreezeMessage::new(GFP_KERNEL)?;
189         let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?;
190 
191         let mut afl_vec_alloc = KVVec::new();
192         let mut info;
193         let mut freeze_entry;
194         let mut node_refs_guard = self.node_refs.lock();
195         loop {
196             let node_refs = &mut *node_refs_guard;
197             info = match node_refs.by_handle.get_mut(&handle) {
198                 Some(info) => info,
199                 None => {
200                     binder_debug!(
201                         UserError,
202                         "BC_REQUEST_FREEZE_NOTIFICATION invalid ref {handle}"
203                     );
204                     return Err(EINVAL);
205                 }
206             };
207             if info.freeze().is_some() {
208                 binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION already set");
209                 return Err(EINVAL);
210             }
211             let node_ref = info.node_ref();
212             freeze_entry = node_refs.freeze_listeners.entry(cookie);
213 
214             if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
215                 if !dupe.get().allow_duplicate(&node_ref.node) {
216                     binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie");
217                     return Err(EINVAL);
218                 }
219             }
220 
221             // Now we add to the node's freeze listener list, with retry and re-allocate if the
222             // vector is full.
223             //
224             // To ensure that the node is added atomically, this is the first time we modify any
225             // state. When this call succeeds, all other modifications must occur without the
226             // possibility for any failure paths.
227             match node_ref
228                 .node
229                 .add_freeze_listener(self, &mut afl_vec_alloc)?
230             {
231                 Ok(()) => break,
232                 Err(resize_target) => {
233                     drop(node_refs_guard);
234                     afl_vec_alloc = KVVec::with_capacity(resize_target, GFP_KERNEL)?;
235                     node_refs_guard = self.node_refs.lock();
236                 }
237             }
238         }
239 
240         match freeze_entry {
241             rbtree::Entry::Vacant(entry) => {
242                 entry.insert(
243                     FreezeListener {
244                         cookie,
245                         node: info.node_ref().node.clone(),
246                         last_is_frozen: None,
247                         is_pending: false,
248                         is_clearing: false,
249                         num_pending_duplicates: 0,
250                         num_cleared_duplicates: 0,
251                     },
252                     alloc,
253                 );
254             }
255             rbtree::Entry::Occupied(mut dupe) => {
256                 let dupe = dupe.get_mut();
257                 if dupe.is_pending {
258                     dupe.num_pending_duplicates += 1;
259                 } else {
260                     dupe.num_cleared_duplicates += 1;
261                 }
262                 dupe.last_is_frozen = None;
263                 dupe.is_pending = false;
264                 dupe.is_clearing = false;
265             }
266         }
267 
268         *info.freeze() = Some(cookie);
269         let msg = FreezeMessage::init(msg, cookie, self.task.pid());
270         drop(node_refs_guard);
271         let _ = self.push_work(msg);
272         Ok(())
273     }
274 
275     pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result<()> {
276         let cookie = FreezeCookie(reader.read()?);
277         let alloc = FreezeMessage::new(GFP_KERNEL)?;
278         let mut node_refs_guard = self.node_refs.lock();
279         let node_refs = &mut *node_refs_guard;
280         let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else {
281             binder_debug!(
282                 UserError,
283                 "BC_FREEZE_NOTIFICATION_DONE {:016x} not found",
284                 cookie.0
285             );
286             return Err(EINVAL);
287         };
288         let mut clear_msg = None;
289         if freeze.num_pending_duplicates > 0 {
290             clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
291             freeze.num_pending_duplicates -= 1;
292             freeze.num_cleared_duplicates += 1;
293         } else {
294             if !freeze.is_pending {
295                 binder_debug!(
296                     UserError,
297                     "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending",
298                     cookie.0
299                 );
300                 return Err(EINVAL);
301             }
302             let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
303             if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) {
304                 // Immediately send another FreezeMessage.
305                 clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
306             }
307             freeze.is_pending = false;
308         }
309         drop(node_refs_guard);
310         if let Some(clear_msg) = clear_msg {
311             let _ = self.push_work(clear_msg);
312         }
313         Ok(())
314     }
315 
316     pub(crate) fn clear_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader) -> Result<()> {
317         let hc = reader.read::<BinderHandleCookie>()?;
318         let handle = hc.handle;
319         let cookie = FreezeCookie(hc.cookie);
320 
321         let _to_free_fl;
322         let alloc = FreezeMessage::new(GFP_KERNEL)?;
323         let mut node_refs_guard = self.node_refs.lock();
324         let node_refs = &mut *node_refs_guard;
325         let Some(info) = node_refs.by_handle.get_mut(&handle) else {
326             binder_debug!(
327                 UserError,
328                 "BC_CLEAR_FREEZE_NOTIFICATION invalid ref {handle}"
329             );
330             return Err(EINVAL);
331         };
332         let Some(info_cookie) = info.freeze() else {
333             binder_debug!(
334                 UserError,
335                 "BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active"
336             );
337             return Err(EINVAL);
338         };
339         if *info_cookie != cookie {
340             binder_debug!(
341                 UserError,
342                 "BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch"
343             );
344             return Err(EINVAL);
345         }
346         let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else {
347             binder_debug!(
348                 UserError,
349                 "BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {handle}"
350             );
351             return Err(EINVAL);
352         };
353         listener.is_clearing = true;
354         _to_free_fl = listener.node.remove_freeze_listener(self);
355         *info.freeze() = None;
356         let mut msg = None;
357         if !listener.is_pending {
358             msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
359         }
360         drop(node_refs_guard);
361 
362         if let Some(msg) = msg {
363             let _ = self.push_work(msg);
364         }
365         Ok(())
366     }
367 
368     fn get_freeze_cookie(&self, node: &DArc<Node>) -> Option<FreezeCookie> {
369         let node_refs = &mut *self.node_refs.lock();
370         let handle = node_refs.by_node.get(&node.global_id())?;
371         let node_ref = node_refs.by_handle.get_mut(handle)?;
372         *node_ref.freeze()
373     }
374 
375     /// Creates a vector of every freeze listener on this process.
376     ///
377     /// Returns pairs of the remote process listening for notifications and the local node it is
378     /// listening on.
379     #[expect(clippy::type_complexity)]
380     fn find_freeze_recipients(&self) -> Result<KVVec<(DArc<Node>, Arc<Process>)>, AllocError> {
381         // Defined before `inner` to drop after releasing spinlock if `push_within_capacity` fails.
382         let mut node_proc_pair;
383 
384         // We pre-allocate space for up to 8 recipients before we take the spinlock. However, if
385         // the allocation fails, use a vector with a capacity of zero instead of failing. After
386         // all, there might not be any freeze listeners, in which case this operation could still
387         // succeed.
388         let mut recipients =
389             KVVec::with_capacity(8, GFP_KERNEL).unwrap_or_else(|_err| KVVec::new());
390 
391         let mut inner = self.lock_with_nodes();
392         let mut curr = inner.nodes.cursor_front_mut();
393         while let Some(cursor) = curr {
394             let (key, node) = cursor.current();
395             let key = *key;
396             let list = node.freeze_list(&inner.inner);
397             let len = list.len();
398 
399             if recipients.spare_capacity_mut().len() < len {
400                 drop(inner);
401                 recipients.reserve(len, GFP_KERNEL)?;
402                 inner = self.lock_with_nodes();
403                 // Find the node we were looking at and try again. If the set of nodes was changed,
404                 // then just proceed to the next node. This is ok because we don't guarantee the
405                 // inclusion of nodes that are added or removed in parallel with this operation.
406                 curr = inner.nodes.cursor_lower_bound_mut(&key);
407                 continue;
408             }
409 
410             for proc in list {
411                 node_proc_pair = (node.clone(), proc.clone());
412                 recipients
413                     .push_within_capacity(node_proc_pair)
414                     .map_err(|_| {
415                         pr_err!(
416                             "push_within_capacity failed even though we checked the capacity\n"
417                         );
418                         AllocError
419                     })?;
420             }
421 
422             curr = cursor.move_next();
423         }
424         Ok(recipients)
425     }
426 
427     /// Prepare allocations for sending freeze messages.
428     pub(crate) fn prepare_freeze_messages(&self) -> Result<FreezeMessages, AllocError> {
429         let recipients = self.find_freeze_recipients()?;
430         let mut batch = KVVec::with_capacity(recipients.len(), GFP_KERNEL)?;
431         for (node, proc) in recipients {
432             let Some(cookie) = proc.get_freeze_cookie(&node) else {
433                 // If the freeze listener was removed in the meantime, just discard the
434                 // notification.
435                 continue;
436             };
437             let msg_alloc = FreezeMessage::new(GFP_KERNEL)?;
438             let msg = FreezeMessage::init(msg_alloc, cookie, proc.task.pid());
439             batch.push((proc, msg), GFP_KERNEL)?;
440         }
441 
442         Ok(FreezeMessages { batch })
443     }
444 }
445 
446 pub(crate) struct FreezeMessages {
447     batch: KVVec<(Arc<Process>, DLArc<FreezeMessage>)>,
448 }
449 
450 impl FreezeMessages {
451     pub(crate) fn send_messages(self) {
452         for (proc, msg) in self.batch {
453             let _ = proc.push_work(msg);
454         }
455     }
456 }
457