11a2ce184d385f43521c11497a66b9cf95d8ea74 Don't use webrtc::Buffer(size_t, size_t) constructor
Triage note: Hardening against uninitialized memory / potential info-leak in a digest buffer. Reachable only via the experimental RtcTransport (RTCPeerConnection transport) API, so not default-config and not a confirmed leak from the diff.
The bug at a glance
This is uninitialized-memory hardening on an experimental code path. The deprecated webrtc::Buffer(size_t size, size_t capacity) constructor sets logical size to ‘size’ while allocating ‘capacity’ bytes; when called as Buffer(0, kMaxDigestSize) it allocates kMaxDigestSize bytes of backing store with logical size 0 and leaves the backing memory uninitialized. If any later code read past the logical size (or serialized the buffer’s capacity region, or resized without zero-filling before use), stale heap bytes could be exposed. The fix swaps to Buffer::CreateWithCapacity(kMaxDigestSize), a factory that returns an empty buffer with reserved capacity and defined initialization semantics, eliminating the uninitialized-backing hazard. Severity is low for three reasons: (1) this lives in modules/peerconnection/rtc_transport, the new experimental RtcTransport API surface which is not broadly shipped; (2) the buffer holds a certificate digest that is computed and written before use, so an actual leak requires a second bug that reads the region before it is populated; and (3) the change is motivated by an upstream WebRTC deprecation (webrtc:422223681) removing a footgun constructor, i.e. defensive de-risking rather than a confirmed live leak. Honestly watch-tier: a real uninitialized-memory footgun removed proactively, with a plausible but unproven info-disclosure path.
The angle is the semantics of the two-argument Buffer constructor. webrtc::Buffer(size, capacity) is a known footgun: it does not zero the allocation, and the gap between logical size and reserved capacity is precisely uninitialized heap. Any digest/serialization buffer built this way is a candidate for leaking adjacent heap contents into a network-visible or script-visible field if the size/capacity bookkeeping is ever off by even one path. The fix removes the ambiguous constructor in favor of an explicit factory whose contract is unambiguous.
Root cause
In RtcTransport’s constructor (rtc_transport.cc, member initializer list), the digest_ member (a webrtc::Buffer used to hold up to kMaxDigestSize bytes of certificate fingerprint) was initialized as digest_(0, kMaxDigestSize). That invokes webrtc::Buffer(size_t size, size_t capacity) with logical size 0 and capacity kMaxDigestSize: it reserves kMaxDigestSize bytes but does not initialize them, and marks the used length as 0. The deprecated constructor’s documented hazard is exactly this ability to create a buffer whose backing store is uninitialized. The fix replaces it with digest_(webrtc::Buffer::CreateWithCapacity(kMaxDigestSize)), the sanctioned factory that returns a size-0 buffer with kMaxDigestSize capacity reserved under defined semantics, so no path can observe uninitialized backing bytes as buffer contents. Functionally the digest is subsequently generated (the certificate is created immediately after via RTCCertificateGenerator::GenerateCertificate) and the digest written into the buffer, so under normal flow the region is populated before read; the fix closes the window where a mis-ordered or partial read would expose stale heap.
Key code
rtc_transport.cc: replace the deprecated uninitializing Buffer(size, capacity) ctor with the defined CreateWithCapacity factory
RtcTransport::RtcTransport(PassKey, ExecutionContext* context)
: ExecutionContextLifecycleObserver(context),
task_runner_(context->GetTaskRunner(TaskType::kNetworking)),
- digest_(0, kMaxDigestSize) {
+ digest_(webrtc::Buffer::CreateWithCapacity(kMaxDigestSize)) {
Patch walkthrough
third_party/blink/renderer/modules/peerconnection/rtc_transport/rtc_transport.cc— Single member-initializer change for digest_. The old form digest_(0, kMaxDigestSize) called webrtc::Buffer’s deprecated (size, capacity) constructor, which reserves capacity bytes without initializing them. The new form digest_(webrtc::Buffer::CreateWithCapacity(kMaxDigestSize)) uses the WebRTC factory that yields a well-defined empty buffer with reserved capacity, removing the uninitialized-backing-store footgun. No other logic changes; the digest is still populated by the certificate-generation flow that follows.
Background
webrtc::Buffer(size_t, size_t) — A two-argument constructor taking (logical size, capacity). It allocates ‘capacity’ bytes and sets used length to ‘size’ but does not zero the backing store, so the region between and around is uninitialized. Being deprecated upstream (webrtc:422223681) precisely because it can create uninitialized data.
Buffer::CreateWithCapacity — The recommended factory returning an empty (size 0) buffer with a reserved capacity, with defined initialization/growth semantics, so callers get reserved space without exposing uninitialized bytes as content.
RtcTransport / certificate digest — RtcTransport is the experimental low-level P2P transport API in Blink’s peerconnection module. digest_ holds the local certificate fingerprint (up to kMaxDigestSize bytes) used in the transport handshake; it is generated right after construction.
Uninitialized-memory info disclosure — When a buffer’s uninitialized backing bytes are later read, serialized, or sent, they can leak adjacent or stale heap contents (pointers, secrets) to a peer or to script, a classic CWE-457/CWE-908 pattern.
Vulnerability window
- upstream deprecation — WebRTC flagged Buffer(size_t, size_t) for removal (webrtc:422223681) because it can produce uninitialized data; downstream call sites are being migrated.
- migration — Committed 2026-01-31 by Harald Alvestrand (r1577753), migrating the RtcTransport digest buffer to CreateWithCapacity as part of that deprecation cleanup.
Proof of concept
No test or exploit was shipped with this commit, and no info leak was demonstrated. Reconstructing a real leak would require the experimental RtcTransport surface to be enabled and a second defect: a code path that reads digest_ (or serializes its reserved region) before the certificate digest is written into it, or that grows/uses the buffer relying on the uninitialized capacity region as valid content. Under the normal flow the digest is generated and stored before use, so on this evidence there is no confirmed disclosure primitive, only the removed footgun. The honest recipe is: enable RtcTransport, instrument digest_ to observe backing bytes between construction and digest population, and audit every reader of digest_ for a pre-population access; absent such a reader there is no exploit. No exploit code is provided because none is warranted and none was shipped.
Exploitation
- precondition — Requires the experimental RtcTransport API to be reachable/enabled; not broadly available, so real attack surface is minimal at commit time.
- primitive — The theoretical primitive is reading uninitialized heap bytes via the digest buffer. It is not self-sufficient: the buffer is populated before normal use, so a leak needs a separate pre-population read or capacity-region serialization bug.
- ceiling — If chained, the ceiling is a bounded (<= kMaxDigestSize) uninitialized-heap read potentially exposed to a WebRTC peer or to script via transport state, i.e. limited info disclosure. No corruption or control-flow impact from this construct alone.
Detection & hunting
For defenders and detection engineers:
- MSan / uninitialized-read sanitizers —
- remaining Buffer(size_t,size_t) call sites —
Audit directions
- peerconnection buffer construction —
- read-before-write of digest_ —
- deprecation completeness —