Back to Chromium

VRP Flags

components/vrp_flags/README.md

153.0.7989.16.2 KB
Original Source

VRP Flags

This component provides a target for controlled read and controlled write that can be sent to a renderer when the --vrp-flags argument is provided to Chrome. See the VRP FAQ for details of how to submit reports making use of the flags.

//components/vrp_flags is a base-like component that can be consumed by other components like //components/viz and //content/browser.

Usage

Test pages must be named poc.html and are served from a local web server running on localhost:8000.

For cross-renderer testing, the single poc.html file serves as both the attacker page and the victim page:

  • Attacker page: Served at http://localhost:8000/poc.html.
  • Victim page: Loaded at http://victim.test:<port_number>/poc.html when a victim renderer is spawned. The attacker can request an arbitrary number of unique port numbers from the browser.

Victim renderers can be spawned directly by the browser in the foreground or background, or instead the attacker can spawn the victim renderer themselves by navigating to http://victim.test:<port_number>/poc.html (e.g. via window.open(...) or inserting an <iframe>).

Scripts inside poc.html can check window.location.hostname to differentiate between attacker logic (on localhost) and victim logic (on victim.test).

html
// --vrp-flags --no-first-run --enable-blink-features=MojoJS

<div id="output"></div>
<div id="controls"></div>

<script type="module">
  import { VrpFlagsFactory, VrpFlagsRemote, VictimDisposition } from './gen/components/vrp_flags/vrp_flags.mojom.m.js';
  function log(msg) {
    console.log(msg);
    const output = document.getElementById('output');
    output.innerHTML += msg + '
';
  }
  function createProcessUI(name, remote, writeLocations, writeValue) {
    const container = document.createElement('div');
    container.className = 'process-section';
    const title = document.createElement('h2');
    title.textContent = name;
    container.appendChild(title);
    const info = document.createElement('p');
    info.innerHTML = `Write Locations: ${writeLocations.join(', ')} 
 Write Value: ${writeValue}`;
    container.appendChild(info);
    const firstLocation = writeLocations.length > 0 ? writeLocations[0] : 0n;
    const btnWrite = document.createElement('button');
    btnWrite.textContent = `Attempt Write at ${firstLocation}`;
    btnWrite.onclick = async () => {
      try {
        log(`[${name}] Sending WriteAttempted for location ${firstLocation}...`);
        const result = await remote.writeAttempted(firstLocation);
        log(`[${name}] WriteAttempted Result: ${result.success}`);
      } catch (e) {
        log(`[${name}] Write Error: ${e}`);
      }
    };
    container.appendChild(btnWrite);
    const btnRead = document.createElement('button');
    btnRead.textContent = `Attempt Read (Dummy Token)`;
    btnRead.onclick = async () => {
      try {
        // Get prefix first, then try to read with a dummy token
        log(`[${name}] Calling GetReadPrefix...`);
        const prefixResult = await remote.getReadPrefix();
        log(`[${name}] Prefix received: high=${prefixResult.prefix.high}, low=${prefixResult.prefix.low}`);
        const dummyToken = { high: 123456789n, low: 987654321n };
        log(`[${name}] Sending ReadAttempted with dummy token...`);
        const readResult = await remote.readAttempted(dummyToken);
        log(`[${name}] ReadAttempted Result: ${readResult.success}`);
      } catch (e) {
        log(`[${name}] Read Error: ${e}`);
      }
    };
    container.appendChild(btnRead);
    document.getElementById('controls').appendChild(container);
  }
  async function run() {
    // If running in victim process on victim.test, act as the victim
    if (window.location.hostname === 'victim.test') {
      log('Running inside victim process on ' + window.location.host);
      return;
    }

    try {
      log("Binding VrpFlagsFactory...");
      const factory = VrpFlagsFactory.getRemote();
      log("Successfully bound VrpFlagsFactory.
");
      // 1. Browser
      try {
        const browserVrp = new VrpFlagsRemote();
        factory.bindBrowserVrpFlags(browserVrp.$.bindNewPipeAndPassReceiver());
        const browserRes = await browserVrp.getWriteLocations();
        createProcessUI('Browser', browserVrp, browserRes.locations, browserRes.value);
      } catch (e) {
        log('<strong>Browser VRP Error:</strong> ' + e);
      }
      // 2. Network
      try {
        const networkVrp = new VrpFlagsRemote();
        factory.bindNetworkVrpFlags(networkVrp.$.bindNewPipeAndPassReceiver());
        const networkRes = await networkVrp.getWriteLocations();
        createProcessUI('Network', networkVrp, networkRes.locations, networkRes.value);
      } catch (e) {
        log('<strong>Network VRP Error:</strong> ' + e);
      }
      // 3. GPU
      try {
        const gpuVrp = new VrpFlagsRemote();
        factory.bindGpuVrpFlags(gpuVrp.$.bindNewPipeAndPassReceiver());
        const gpuRes = await gpuVrp.getWriteLocations();
        createProcessUI('GPU', gpuVrp, gpuRes.locations, gpuRes.value);
      } catch (e) {
        log('<strong>GPU VRP Error:</strong> ' + e);
      }
      // 4. Victim Renderer
      try {
        const victimVrp = new VrpFlagsRemote();
        // Automatic spawn (foreground tab):
        const res = await factory.startRendererForVrpFlags(
            VictimDisposition.kSpawnForegroundTab,
            victimVrp.$.bindNewPipeAndPassReceiver());
        log('Spawned victim renderer on port ' + res.port);

        // Alternatively, for manual spawn (e.g. via window.open or iframe):
        // const manualVrp = new VrpFlagsRemote();
        // const manualRes = await factory.startRendererForVrpFlags(
        //     VictimDisposition.kManualSpawn,
        //     manualVrp.$.bindNewPipeAndPassReceiver());
        // window.open(`http://victim.test:${manualRes.port}/poc.html`);

        const victimRes = await victimVrp.getWriteLocations();
        createProcessUI('Victim Renderer', victimVrp, victimRes.locations, victimRes.value);
      } catch (e) {
        log('<strong>Victim Renderer VRP Error:</strong> ' + e);
      }
    } catch (e) {
      log('<strong style="color:red">Error:</strong> ' + e);
    }
  }
  run();
</script>