nccgroup / Cartographer

Code Coverage Exploration Plugin for Ghidra
Apache License 2.0
317 stars 26 forks source link

Fix crash when external functions are covered #13

Open krisi0903 opened 1 month ago

krisi0903 commented 1 month ago

We recently encountered a crash when we were using Cartographer to analyze coverage for embedded firmware:

A NullPointerException is thrown when loading a coverage file, the relevant stack trace is below:

Cannot invoke "cartographer.CoverageFunction.addCoverageBlock(ghidra.program.model.address.Address, java.lang.Integer)" because "ccFunc" is null
java.lang.NullPointerException: Cannot invoke "cartographer.CoverageFunction.addCoverageBlock(ghidra.program.model.address.Address, java.lang.Integer)" because "ccFunc" is null
    at cartographer.CoverageFile.populateBlocks(CoverageFile.java:588)
    at cartographer.CartographerPlugin.loadCoverageFile(CartographerPlugin.java:552)
    at cartographer.CartographerPlugin.processCoverageFile(CartographerPlugin.java:587)
    at cartographer.CartographerPlugin$2.lambda$actionPerformed$0(CartographerPlugin.java:303)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
    at cartographer.CartographerPlugin$2.actionPerformed(CartographerPlugin.java:291)
    at docking.DockingActionProxy.actionPerformed(DockingActionProxy.java:47)
    at docking.MenuBarMenuHandler.lambda$processMenuAction$0(MenuBarMenuHandler.java:60)
    at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
    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)

---------------------------------------------------
Build Date: 2024-Apr-10 1518 EDT
Ghidra Version: 11.0.3
Java Home: /usr/lib/jvm/java-21-openjdk-amd64
JVM Version: Ubuntu 21.0.3
OS: Linux 6.8.0-36-generic amd64

I found that the crash is caused when Cartographer tries to add a basic block that is inside a function that is classified as External by Ghidra. In the CartographerPlugin.LoadCoverageFile function, all functions are collected using the iterator currentProgram.getFunctionManager().getFunctions(true), which does not include external functions. When a BB is processed whose address matches the entrypoint of an external function, the call program.getFunctionManager().getFunctionContaining(address) returns this external function. This is not present in the ccFunctionMap, so the map lookup returns null and we get a NPE.

Admittedly, having coverage for external functions is a bit of an edge-case. In our situation, we were analyzing a firmware that is split into a secure and non-secure part which are both provided as separate binaries, but run on the same system. Therefore we have a coverage file that contains coverage over both parts. In this case, one of the functions calling from non-secure into secure mode was defined as an external symbol in the non-secure binary. I am not able to share the binaries since they are under NDA, but I have created a simple example binary and coverage file to reproduce the issue.

sample.zip

The crash can be resolved by checking whether the covered function is external and skipping the BB addition