Skip to content

Use-after-free caused by Java_org_cef_browser_CefMessageRouter_1N_N_1RemoveHandler #538

@ksaweryr

Description

@ksaweryr

Describe the bug
Java_org_cef_browser_CefMessageRouter_1N_N_1RemoveHandler decrements the reference counter for MessageRouterHandler object before returning. As a result, the object's refcount can drop to 0, causing it to be freed; if a handler uses CefMessageRouter::RemoveHandler to remove itself from a message router, the router will keep a (now dangling) reference to the handler in the browser query info map (https://bitbucket.org/chromiumembedded/cef/src/cfff1e30212d0c208ac0d0eb3d033c6af7133449/libcef_dll/wrapper/cef_message_router.cc#lines-320 and https://bitbucket.org/chromiumembedded/cef/src/cfff1e30212d0c208ac0d0eb3d033c6af7133449/libcef_dll/wrapper/cef_message_router.cc#lines-341:344). If the handler also triggers OnBeforeBrowse, pending query cleanup (https://bitbucket.org/chromiumembedded/cef/src/cfff1e30212d0c208ac0d0eb3d033c6af7133449/libcef_dll/wrapper/cef_message_router.cc#lines-283:288) could happen before the callback resolves and removes the dangling reference from query info map, causing a method lookup to be performed on a vtable of a stale object (https://bitbucket.org/chromiumembedded/cef/src/cfff1e30212d0c208ac0d0eb3d033c6af7133449/libcef_dll/wrapper/cef_message_router.cc#lines-514) - on Linux with glibc, this can cause a segfault.

Additionally, even though it seems virtually impossible due to the ridiculous setup required, this bug could be used to replace the vtable of the dangling handler, leading to arbitrary code execution.

To Reproduce
Steps to reproduce the behavior:

  1. Compile and run the code attached below.
  2. Press "edit" href a bunch of times.
import org.cef.CefApp;
import org.cef.CefClient;
import org.cef.CefSettings;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.browser.CefMessageRouter;
import org.cef.callback.CefQueryCallback;
import org.cef.handler.CefMessageRouterHandler;
import org.cef.handler.CefMessageRouterHandlerAdapter;
import org.cef.handler.CefRequestHandlerAdapter;
import org.cef.network.CefRequest;

import javax.swing.*;
import java.awt.*;

class UafComponent {
    int i = 0;
    CefBrowser browser;
    CefMessageRouter router;
    CefMessageRouterHandler handler;

    public UafComponent(CefBrowser browser, CefMessageRouter router, CefMessageRouterHandler handler) {
        this.browser = browser;
        this.router = router;
        this.handler = handler;

        router.addHandler(handler, true);
        browser.getClient().addMessageRouter(router);
    }

    public void updateHTML() {
        router.removeHandler(handler);
        System.out.println("Handler removed!");
        handler = new CefMessageRouterHandlerAdapter() {
            @Override
            public boolean onQuery(CefBrowser browser, CefFrame frame, long queryId, String request, boolean persistent, CefQueryCallback callback) {
                System.out.println("onQuery" + i);
                updateHTML();
                try {
                    Thread.sleep(1000);
                } catch (Exception ex) {
                    throw new RuntimeException(ex);
                }
                callback.success("onQuery" + i);
                return true;
            }
        };
        i++;
        router.addHandler(handler, true);
        browser.loadURL("data:text/html;charset=utf-8,<a href=\"javascript:cefQuery({request:&quot;test&quot;, onSuccess: function(response) {}, onFailure: function(errorCode, errorMessage) {console.error(&quot;Error thrown:&quot;, errorCode, errorMessage)}});\">edit</a>");
    }
}

public class Main {
    public static void main(String[] args) {
        System.loadLibrary("cef");
        System.loadLibrary("jcef");
        SwingUtilities.invokeLater(() -> {
            // Cef settings
            CefSettings settings = new CefSettings();
            settings.windowless_rendering_enabled = false;

            // Initialize the CefApp with settings
            CefApp cefApp = CefApp.getInstance(settings);
            CefClient client = cefApp.createClient();

            client.addRequestHandler(new CefRequestHandlerAdapter() {
                @Override
                public boolean onBeforeBrowse(CefBrowser browser, CefFrame frame, CefRequest request, boolean userGesture, boolean isRedirect) {
                    System.out.println("onBeforeBrowse");
                    return false;
                }
            });

            // Create the browser
            CefBrowser browser = client.createBrowser("data:text/html;charset=utf-8,<a href=\"javascript:cefQuery({request:&quot;test&quot;, onSuccess: function(response) {}, onFailure: function(errorCode, errorMessage) {console.error(&quot;Error thrown:&quot;, errorCode, errorMessage)}});\">edit</a>", false, false);

            UafComponent component = new UafComponent(browser, CefMessageRouter.create(), new CefMessageRouterHandlerAdapter() {});

            component.updateHTML();

            // Embed in Swing UI
            Component browserUI = browser.getUIComponent();
            browserUI.setFocusable(true);
            browserUI.requestFocusInWindow();

            JFrame frame = new JFrame("Interactive JCEF Browser");
            frame.getContentPane().add(browserUI, BorderLayout.CENTER);
            frame.setSize(800, 600);
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            frame.setVisible(true);

            // Ensure it gets focus
            browserUI.requestFocusInWindow();
        });
    }
}

Expected behavior
Expected behaviour: the app never crashes; actual behaviour: the app sometimes crashes.

Screenshots
If applicable, add screenshots to help explain your problem.

Versions (please complete the following information):

  • OS: Debian 13
  • Java Version: OpenJDK 21.0.11
  • JCEF Version: Current master branch (commit 6d3e8ca)
  • CEF Version: Current master branch (commit cfff1e30212d0c208ac0d0eb3d033c6af7133449)

Additional context

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug report

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions