b5ec42654dd4d18ae833b0615d78a204eec87dbc [M145][PEPC] Align UserMediaElement configuration with PermissionElement
Triage note: M145 backport aligning a permission-UI element's origin-trial gating with PermissionElement; prevents an OT token enabling the element in an unsupported/half-configured state. Configuration hardening, not a memory bug.
The bug at a glance
This is a defense-in-depth feature-gating alignment, not a memory-safety bug, so it is watch-tier not patch-now. The concern it closes is a state mismatch: an origin-trial (OT) token could enable the renderer-side UserMediaElement permission element on a platform or configuration where the base feature kUserMediaElement is disabled and the browser process therefore provides no backing support. Before this CL, the renderer’s OT validation for the ‘UserMediaElement’ trial did not consult the base FeatureList, and the content layer had no gate that forced the runtime flag off when the feature was disabled — so an OT token alone could turn the element on in the renderer while the browser side was absent. That is a policy-consistency and robustness defect (mismatched enablement leading at worst to broken permission UI or unexpected surface exposure of an experimental permission element), which is why it rates low with a modest CVSS (3.7). It is not patch-now: there is no corruption, no privilege boundary crossed by the mismatch itself, exploitation requires an attacker-issued valid OT token, and the element is an experimental permission surface still gated behind the trial. The fix simply makes UserMediaElement obey the exact same enablement discipline PermissionElement already follows.
For a defender the takeaway is about origin-trial trust boundaries, not a discrete vulnerability. Origin trials let a site opt into experimental features by embedding a signed token; the renderer historically could enable some trials on token presence alone. When a feature is a permission surface (here, a declarative permission element for getUserMedia-style camera/mic access) that requires browser-process cooperation, enabling it in the renderer without the browser process being ready produces a half-wired feature. This CL aligns UserMediaElement with the PermissionElement pattern so that (a) the content layer force-disables the runtime flag whenever the base feature is off, and (b) the renderer’s OT validation refuses to honor the trial unless the base feature is enabled. Auditors should treat ‘OT token can enable a permission/capability element independently of its base FeatureList gate’ as the anti-pattern being stamped out.
Root cause
The root cause is that UserMediaElement’s enablement plumbing was incomplete relative to the established PermissionElement pattern, allowing origin-trial activation to diverge from base-feature policy. Three coordinated gaps are closed.
First, in content/child/runtime_features.cc a new guard is added, immediately after the analogous PermissionElement guard: if base::FeatureList::IsEnabled(blink::features::kUserMediaElement) is false, the code logs a warning when the runtime flag was requested and calls WebRuntimeFeatures::EnableUserMediaElement(false). The comment states plainly that ‘UserMediaElement cannot be enabled without the support of the browser process.’ This is the content-layer backstop that prevents the renderer from believing the element is enabled when the browser side is disabled.
Second, in third_party/blink/renderer/core/origin_trials/origin_trial_context.cc, a new branch is added to the trial-name validation: for trial_name == ‘UserMediaElement’, it now returns base::FeatureList::IsEnabled(blink::features::kUserMediaElement). This ties honoring the origin trial to the base feature being enabled — an OT token no longer suffices on its own; the base feature must also be on. Again this mirrors the immediately preceding PermissionElement case.
Third, in runtime_enabled_features.json5, the UserMediaElement entry gains public: true. In Blink’s feature metadata this exposes the feature so the OT/base-feature validation machinery (and the WebRuntimeFeatures accessors like IsUserMediaElementEnabledByRuntimeFlag / EnableUserMediaElement referenced by the content-layer guard) are generated and usable. Together the three changes ensure that on platforms/configurations where kUserMediaElement is disabled, neither a command-line runtime flag nor an origin-trial token can bring the renderer-side element up out of sync with the browser process.
Key code
Content-layer backstop plus OT validation tie the element to the base FeatureList, matching PermissionElement
+ // UserMediaElement cannot be enabled without the support of the
+ // browser process.
+ if (!base::FeatureList::IsEnabled(blink::features::kUserMediaElement)) {
+ LOG_IF(WARNING,
+ WebRuntimeFeatures::IsUserMediaElementEnabledByRuntimeFlag())
+ << "UserMediaElement cannot be enabled in this configuration. Use --"
+ << switches::kEnableFeatures << "="
+ << blink::features::kUserMediaElement.name << " instead.";
+ WebRuntimeFeatures::EnableUserMediaElement(false);
+ }
+ if (trial_name == "UserMediaElement") {
+ return base::FeatureList::IsEnabled(blink::features::kUserMediaElement);
+ }
Patch walkthrough
content/child/runtime_features.cc— Adds a content-layer gate right after the PermissionElement gate: when kUserMediaElement is disabled, warn if the runtime flag was set and force WebRuntimeFeatures::EnableUserMediaElement(false). This is the authoritative backstop ensuring the renderer never treats the element as enabled without browser-process support.third_party/blink/renderer/core/origin_trials/origin_trial_context.cc— Adds a validation branch so the ‘UserMediaElement’ origin trial is only honored when base::FeatureList::IsEnabled(kUserMediaElement) is true. This prevents an OT token from enabling the element on a platform where the base feature is off, closing the token-alone enablement path.third_party/blink/renderer/platform/runtime_enabled_features.json5— Marks the UserMediaElement feature public: true, exposing it to the generated feature/OT validation machinery and the WebRuntimeFeatures accessors the other two files depend on. Without this the content-layer and OT checks could not reference the feature consistently.
Background
PEPC / permission element — Page-Embedded Permission Control is a Blink initiative giving sites declarative HTML elements to request permissions with a browser-trusted UI. PermissionElement is the general one; UserMediaElement is the camera/microphone-oriented variant. These elements require browser-process cooperation to render trusted permission UI and gate capability grants.
Origin trial (OT) — A mechanism letting a site enable an experimental feature by embedding a signed token in a header or meta tag. The renderer’s OriginTrialContext validates the token and may enable the corresponding runtime feature. The security question is whether token presence alone should be sufficient, or whether it must be additionally gated on the base FeatureList.
base_feature_status / FeatureList gate — runtime_enabled_features.json5 declares whether a feature’s base state can be overridden by field trials or origin trials. Aligning OT honoring with base::FeatureList::IsEnabled ensures a single source of truth for whether the capability is actually wired up on this platform/channel.
renderer/browser enablement mismatch — If the renderer enables a capability element the browser process does not support, the feature is half-wired: UI or DOM surface may appear while the enforcing/backing logic is absent. Aligning both sides prevents inconsistent security-relevant state around permission grants.
Vulnerability window
- Root cause — UserMediaElement was added following PermissionElement but without the matching content-layer force-disable and OT base-feature validation, leaving OT/runtime-flag enablement able to diverge from the base FeatureList.
- Fixed on main — Original commit 239e57690c5ec8d6f98962393f412d14e6f2c861, authored by Thomas Nguyen, reviewed by Dave Tapuska. Tracked under Bug 476145564, Fixed 477959107. Landed main@{#1571277} on 2026-01-23.
- Backport [M145] — This artifact (b5ec42654d) is the M145 cherry-pick of 239e5769, landed on branch-heads/7632@{#835}, Rubber-Stamper approved. The [M145] tag and ‘cherry picked from’ line confirm it is a stable-branch backport, indicating the alignment was deemed worth shipping to the released milestone rather than trunk-only.
Proof of concept
No test ships in this diff. There is no exploit and none is claimed. To exercise the pre-patch behavior a researcher would: obtain a valid ‘UserMediaElement’ origin-trial token, load it on a platform/build where the kUserMediaElement base feature is disabled, and observe whether the renderer instantiates the UserMediaElement despite the browser process lacking support (a state mismatch). Post-patch both the content-layer gate and the OT validation force the element off in that configuration. No memory-safety consequence is demonstrated; the observable effect is limited to whether the experimental element is (incorrectly) surfaced.
Exploitation
- Precondition — Attacker must possess a valid, unexpired origin-trial token for ‘UserMediaElement’ for their origin — a real gating cost, since tokens are issued per-origin and can be revoked. The feature is also experimental, limiting the population of affected clients.
- Trigger mismatch (pre-patch only) — On a configuration with the base feature disabled, the token could enable the renderer-side element without browser-process backing. The concrete impact is a half-wired experimental permission surface, not a memory-corruption or a direct permission escalation; the browser process still ultimately mediates any actual capability grant.
- Blockers — No privilege boundary is crossed by the mismatch alone, exploitation is fully gated on token issuance, and the worst realistic outcome is inconsistent/experimental UI state. This is why it is watch-tier hardening rather than an exploitable finding.
Detection & hunting
For defenders and detection engineers:
- Warning log: ‘UserMediaElement cannot be enabled in this configuration’ — Post-patch this LOG(WARNING) fires when the runtime flag/OT requested the element on a build where the base feature is off. Its presence indicates configuration attempts to enable the element out of policy — worth noting in fleet telemetry.
- Origin-trial tokens for ‘UserMediaElement’ on unexpected origins — Monitor for OT tokens naming the UserMediaElement/PEPC trials, especially on origins that should not be running the experiment; correlate with permission-element instantiation.
Audit directions
- All origin-trial-gated permission/capability elements — For every trial that fronts a browser-process-backed capability, verify OriginTrialContext validation returns base::FeatureList::IsEnabled() and that content/child/runtime_features.cc has a matching force-disable. Any trial honored on token presence alone is the pattern this CL corrected.
- runtime_enabled_features.json5 consistency — Check that features referenced by content-layer WebRuntimeFeatures accessors are marked public and have their OT-os / base_feature_status fields consistent with the platforms where the browser process actually supports them, to prevent renderer/browser enablement divergence.