GhidraJupyter / ghidra-jupyter-kotlin

MIT License
115 stars 10 forks source link

Null Program location exception after checking-in a file #8

Closed rompgadgets closed 2 years ago

rompgadgets commented 3 years ago

I noticed after installing the plug-in for Ghidra 9.2.2 that after checking in a file the CodeBrowser reports Null Program Location set repeatedly until the CodeBrowser window is closed and re-opened. Made a quick fix as I believe the root issue is that after a check-in the program closed event is triggered nulling out the state variables, but the program activation event is never called again.

fmagin commented 3 years ago

You are talking about the version control checks I assume? This is indeed something I never tested, highly likely that I missed something there. My gut feeling is that there is probably some other event that the plugin should handle, instead of doing this in the locationChanged callback. Will look into this and see if your fix is the best solution. Thank you very much for reporting this!

rompgadgets commented 3 years ago

Yeah, exactly during the version control checks. Agreed, it seems like there should be another explicit event to handle file check-ins.
Sure, no problem.

fmagin commented 2 years ago

Okay, I finally got around to properly looking into this. I am using Ghidra 10.1 and the ghidra-server.org Server which is also Ghidra 10.1. What happens is that when a program is opened (i.e. the Codebrowser tool is open), something is changed and the binary is checked in while the code browser is running the events seem to fire in the following order:

INFO  Checkin completed for xochitl (GhidraFileData)  
INFO  Program opened (JupyterKotlinPlugin)  
INFO  Program deactivated (JupyterKotlinPlugin)  
INFO  Selection changed to null (JupyterKotlinPlugin)  
INFO  Highlight changed (JupyterKotlinPlugin)  
INFO  Program activated (JupyterKotlinPlugin)
INFO  Location changed to MemoryBlockStartFieldLocation@00010000, row=0, col=0, charOffset=0, Comment Type = -1 (JupyterKotlinPlugin)  
INFO  Program closed (JupyterKotlinPlugin)  

Afterwards clicking anywhere (and changing the location) will result in an error titled Null program passed to ProgramLocation. Trace and remove this problem with the traceback:

Null program passed to ProgramLocation
java.lang.NullPointerException: Null program passed to ProgramLocation
    at ghidra.program.util.ProgramLocation.<init>(ProgramLocation.java:72)
    at ghidra.program.util.ProgramLocation.<init>(ProgramLocation.java:128)
    at ghidra.app.script.GhidraState.setCurrentAddress(GhidraState.java:141)
    at ghidra.app.script.GhidraScript.setCurrentLocation(GhidraScript.java:501)
    at GhidraJupyterKotlin.JupyterKotlinPlugin.locationChanged(JupyterKotlinPlugin.java:317)
    at ghidra.app.plugin.ProgramPlugin.processEvent(ProgramPlugin.java:173)
    at ghidra.framework.plugintool.Plugin.eventSent(Plugin.java:329)
    at ghidra.framework.plugintool.mgr.EventManager.sendEvents(EventManager.java:286)
    at ghidra.framework.plugintool.mgr.EventManager.lambda$new$3(EventManager.java:49)
    at ghidra.util.Swing.doRun(Swing.java:292)
    at ghidra.util.Swing.runNow(Swing.java:208)
    at ghidra.util.Swing.runNow(Swing.java:163)
    at ghidra.framework.plugintool.mgr.EventManager.fireEvent(EventManager.java:216)
    at ghidra.framework.plugintool.PluginTool.firePluginEvent(PluginTool.java:475)
    at ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin.locationChanged(CodeBrowserPlugin.java:276)
    at ghidra.app.plugin.core.codebrowser.CodeViewerProvider.programLocationChanged(CodeViewerProvider.java:521)
    at ghidra.app.util.viewer.listingpanel.ListingPanel.fieldLocationChanged(ListingPanel.java:797)
    at docking.widgets.fieldpanel.FieldPanel$CursorHandler.notifyCursorChanged(FieldPanel.java:2133)
    at docking.widgets.fieldpanel.FieldPanel$CursorHandler.setCursorPos(FieldPanel.java:1928)
    at docking.widgets.fieldpanel.FieldPanel$FieldPanelMouseAdapter.mousePressed(FieldPanel.java:1316)
    at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
    at java.desktop/java.awt.AWTEventMulticaster.mousePressed(AWTEventMulticaster.java:287)
    at java.desktop/java.awt.Component.processMouseEvent(Component.java:6632)
    at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3342)
    at java.desktop/java.awt.Component.processEvent(Component.java:6400)
    at java.desktop/java.awt.Container.processEvent(Container.java:2263)
    at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5011)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4843)
    at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
    at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4544)
    at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
    at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2772)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4843)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:743)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

So for some reason there is a program closed event after all the other program opening and activation. I think the actual root cause was that we weren't implementing programClosed properly. The argument to it isn't necessarily the program that was set opened and activated earlier. For example FunctionGraphPlugin.programClosed explicitly checks if the passed argument is even the program that is considered "current"

    @Override
    protected void programClosed(Program program) {
        if (currentProgram == program) {
            currentProgram = null;
        }

        connectedProvider.programClosed(program);

        Iterator<FGProvider> iterator = disconnectedProviders.iterator();
        while (iterator.hasNext()) {
            FGProvider provider = iterator.next();
            if (provider.getProgram() == program) {
                iterator.remove();
                removeProvider(provider);
            }
        }

adding the same kind of check to the JupyterKotlinPlugin.programClosed seems to work, because the argument is the old program being closed, while the new one was activated earlier already.

Currently the Kotlin Kernel plugin doesn't really manage multiple programs, so this fix should be sufficient and fixes the addresses the actual root cause. Still, your PR might have been an acceptable workaround ( I guess it at least worked for you), so I am kinda sorry that I let perfect be the enemy of good here, and only addressed this 8 months later :/