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.
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:
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 iteratorcurrentProgram.getFunctionManager().getFunctions(true)
, which does not include external functions. When a BB is processed whose address matches the entrypoint of an external function, the callprogram.getFunctionManager().getFunctionContaining(address)
returns this external function. This is not present in theccFunctionMap
, 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