Bluetooth Low Energy host stack in pure OCaml
0

Configure Feed

Select the types of activity you want to include in your feed.

bluetooth: add Reassembly.forget to reap a handle on disconnection

forget releases a handle's partially received PDU, reaping a reassembly that will never complete because the link was torn down (an HCI Disconnection Complete for that handle). Without it a handle that starts a multi-fragment PDU and never finishes retains its buffer forever -- one leak per churned short-lived BLE link. Pure and immutable, it completes the reassembler's lifecycle alongside active_handles.

The pure Bluetooth host reaps structurally: it holds the reassembler in the per-connection record and drops that record on Disconnection Complete, so a fresh connection on a reused handle starts empty and no partial carries over. forget is the reap primitive for a driver that shares one reassembler across handles (the fuzz model), and hardens the reassembler as a standalone table.

Also threads the survivor's post-completion state in the disconnection-reap fuzz added by the previous commit: a `_` pattern discarded the reaped state, so the final reap-to-baseline check saw a stale entry once forget existed.

+11 -2
+3 -2
fuzz/fuzz_reassembly.ml
··· 156 156 let last = n - 1 in 157 157 let p = payload_of last in 158 158 (match R.incoming !t ~handle:last ~pb:Pb.Continuing (String.sub p 1 1) with 159 - | Ok (_, [ f ]) -> 159 + | Ok (t', [ f ]) -> 160 160 if f.Frame.payload <> p then 161 161 failf "surviving handle %d payload corrupted by reaps" last; 162 - if f.Frame.cid <> cid then failf "surviving handle %d cid corrupted" last 162 + if f.Frame.cid <> cid then failf "surviving handle %d cid corrupted" last; 163 + t := t' 163 164 | Ok (_, l) -> failf "surviving handle %d: %d frames" last (List.length l) 164 165 | Error (`Msg m) -> failf "surviving handle %d completion: %s" last m); 165 166 (* The survivor was released on completion, so the table is back to baseline
+1
lib/l2cap/reassembly.ml
··· 6 6 7 7 let v () = { partial = [] } 8 8 let active_handles t = List.length t.partial 9 + let forget t ~handle = { partial = List.remove_assoc handle t.partial } 9 10 let is_first = function Pb.Continuing -> false | _ -> true 10 11 11 12 let incoming t ~handle ~pb fragment =
+7
lib/l2cap/reassembly.mli
··· 18 18 once the PDU it started is delivered, so this returns to zero whenever no 19 19 reassembly is in progress. *) 20 20 21 + val forget : t -> handle:int -> t 22 + (** [forget t ~handle] releases any partially received PDU held for [handle], 23 + reaping a reassembly that will never complete because the link was torn down 24 + (an HCI Disconnection Complete for [handle]). A no-op when [handle] holds no 25 + partial. Without this a handle that starts a PDU and never finishes retains 26 + its buffer forever, leaking one partial per dropped link. *) 27 + 21 28 val incoming : 22 29 t -> 23 30 handle:int ->