-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Description
Describe the bug
When trying to render a variable made of a stack varnode first (MSBytes) then registers varnodes (LSBytes), Ghidra returns the following error: Storage does not have a stack varnode
. From searching the Ghidra codebase the issue may arise from an oversight in the VariableStorage
class.
This only affects little endian languages (see Additional context)
To Reproduce
I've encountered this issue while developing an extension that adds support for the Blackfin+ architecture. I tried to find another implementation that was already in Ghidra to replicate the bug, to no avail. As such the steps to reproduce the issue includes using said extension. This is far from ideal, but, the extension is quite simple and shouldn't be too hard to parse if there's something wrong with the .cspec
.
Steps to reproduce the behavior:
- Download the Blackfin+ Extension: https://github.com/hekar-lab/Ghidra-BlackFinPlus/releases/tag/v0.1
- Download the example program file attached with this issue
- Install the extension (on Ghidra 11.3.2)
- Import and analyse the program
- Set the function signature of the
llmult
function to the following:
longlong llmult (longlong x, longlong y)
- Error should pop up on instruction that include a stack access at the
y
var address.
Expected behavior
There should be no error and the instruction's operands should render normally.
Attachments
The ZIP archive containing the program used for the reproduction of the issue:
bug.zip
Environment (please complete the following information):
- OS: Fedora 42
- Java Version: 21
- Ghidra Version: 11.3.2
- Ghidra Origin: official GitHub distro
Additional context
I looked into the Ghidra codebase to find where the issue might lie.
It happens when Ghidra tries to render the operands of an instruction containing a variable that joins the stack with a register. This configuration messes with how VariableStorage
checks if a variable is/has stack storage.
Here's how VariableStorage
does those checks:
public boolean hasStackStorage() {
...
Address storageAddr = getLastVarnode().getAddress();
return storageAddr.isStackAddress();
}
public boolean isStackStorage() {
...
Address storageAddr = getFirstVarnode().getAddress();
return storageAddr.isStackAddress();
}
Meaning it expects the variable to have varnodes arranged like so [register, ..., stack]
. Which works for big endian languages. But not little endian ones due to the fact that the varnodes are in reverse order.
Same thing happens when retrieving the stack offset which in turns causes the displayed errors:
public int getStackOffset() {
if (varnodes != null && varnodes.length != 0) {
Address storageAddr = getLastVarnode().getAddress();
if (storageAddr.isStackAddress()) {
return (int) storageAddr.getOffset();
}
}
throw new UnsupportedOperationException("Storage does not have a stack varnode");
}