← All reports

[SecurityFlags] Introduce SecurityFlags and propagate them to the privileged child processes

Component: WebKit process infrastructure | 722d0b3

Source/WTF/Scripts/GenerateSecurityFlags.rb

+# Keys as the file spells them, in order and including repeats. Validation cannot use the hash YAML.load_file
+# returns, because YAML silently collapses a repeated key and keeps only the last one.
+def keysInFileOrder(path)
+ ...
+end
+
+# Keys sort by magnitude: a plain string sort would put radar1000000000 before radar99999999.
+def radarNumber(name)
+ digits = name[/\Aradar([1-9][0-9]*)\z/, 1]
+ digits && digits.to_i
+end
+
+def load(path)
+ seen = {}
+ previousName = nil
+
+ keysInFileOrder(path).each do |name|
+ if seen[name]
+ STDERR.puts "error: Input file #{path} defines '#{name}' more than once. Only the last one would survive, silently discarding the other entry."

WebKit's process model treats WebContent as the untrusted, sandboxed process and Networking, GPU and Model as more privileged; IPC between the UIProcess and these children is the trust boundary that security mitigations rely on. This commit adds a SecurityFlags mechanism: a YAML-defined bitset of named boolean flags, keyed by radar number, that WebKit can disable at runtime, propagated from a singleton controller in the UIProcess to those privileged children via creation parameters and a new SecurityFlagsDidChange IPC message.

Only the plumbing lands here — one placeholder flag exists, nothing consumes it, and there is no server transport. It rides the same creation-parameters/live-update pattern used elsewhere (SharedPreferencesForWebProcessDidChange) but intentionally omits per-connection scoping and reply acknowledgment, defaulting fail-safe (enforced) until an update arrives. Storage is a WTF::BitSet serialized to a fixed-width wire format via a validating factory, and reads happen through a relaxed atomic word load so GPU-process stream receivers on non-main threads can check flags without locking.

This is the WebKit-side half of a remote kill-switch that lets a shipped security fix be disabled in the field without a software update — powerful, and double-edged, since it means a mitigation's enforcement can be turned off remotely. It deliberately excludes the WebContent process (treated as compromised) and gates the disable API behind ENGINEERING_BUILD, so those two boundaries are the ones worth understanding before probing process-trust assumptions elsewhere.

The forward-facing pattern is a live-update IPC message that mutates security-relevant state without connection scoping or acknowledgment. Narrow: SecurityFlagsDidChange has no ProcessIdentifier scoping and no reply, unlike its SharedPreferences analogue — the match tell is a message receiver that writes into a process-global singleton with no check of which connection sent it. Wider: enumerate the other UIProcess→child live-update messages that mutate global policy state (SharedPreferencesForWebProcessDidChange, sandbox-extension grants, sandbox parameter updates) and check each for the same scoping gap; a compromised privileged process that can send one of these to itself or a sibling is the shape to look for. Widest: the wire-format validating factory is the general "reject a value with a bit belonging to no flag" pattern — audit it and its analogues for off-by-one against the underlying container's actual word width, since a bitset's word count and its declared bit count diverge as soon as the flag list is not a multiple of the word size; and check whether build-configuration gates like ENGINEERING_BUILD on a disable SPI hold consistently across every configuration that ships to users, since a gate enforced in one build system and not another is indistinguishable from no gate.