Skip to content

Commit c502e73

Browse files
committed
Filter out functions without instructions
1 parent 970fb25 commit c502e73

3 files changed

Lines changed: 84 additions & 18 deletions

File tree

src/main/java/ai/reveng/toolkit/ghidra/binarysimilarity/ui/functionselection/FunctionSelectionPanel.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ public class FunctionSelectionPanel extends JPanel {
2424
private final FunctionSelectionTableModel tableModel;
2525
private final GhidraFilterTable<FunctionRowObject> filterTable;
2626
private final JLabel summaryLabel;
27+
private final JLabel warningLabel;
2728

2829
public FunctionSelectionPanel(ServiceProvider serviceProvider) {
2930
super(new BorderLayout());
3031

3132
tableModel = new FunctionSelectionTableModel(serviceProvider);
3233
filterTable = new GhidraFilterTable<>(tableModel);
3334
summaryLabel = new JLabel();
35+
warningLabel = new JLabel();
36+
warningLabel.setForeground(new Color(0xCC7700));
37+
warningLabel.setVisible(false);
3438

3539
buildInterface();
3640

@@ -83,7 +87,12 @@ private void buildInterface() {
8387
topPanel.add(toolbarPanel, BorderLayout.WEST);
8488
topPanel.add(summaryPanel, BorderLayout.EAST);
8589

86-
add(topPanel, BorderLayout.NORTH);
90+
JPanel headerPanel = new JPanel();
91+
headerPanel.setLayout(new BoxLayout(headerPanel, BoxLayout.Y_AXIS));
92+
headerPanel.add(topPanel);
93+
headerPanel.add(warningLabel);
94+
95+
add(headerPanel, BorderLayout.NORTH);
8796
add(filterTable, BorderLayout.CENTER);
8897

8998
updateSummaryLabel();
@@ -146,9 +155,15 @@ public void applyRemoteFunctionInfo(List<FunctionInfo> remoteFunctions) {
146155

147156
for (FunctionRowObject row : tableModel.getAllRows()) {
148157
// Reset state from any previous matching
149-
row.setEnabled(true);
150158
row.setRemoteFunctionInfo(null);
151159

160+
// Keep 1-byte functions permanently disabled
161+
if (row.getSize() <= 1) {
162+
row.setEnabled(false);
163+
continue;
164+
}
165+
166+
row.setEnabled(true);
152167
long localAddr = row.getAddress().getOffset();
153168
FunctionInfo match = byAddress.get(localAddr);
154169
if (match != null && sizeMatches(row, match)) {
@@ -182,5 +197,15 @@ private void updateSummaryLabel() {
182197
} else {
183198
summaryLabel.setText(String.format("%d of %d functions selected", selected, total));
184199
}
200+
201+
int tooSmall = tableModel.getTooSmallCount();
202+
if (tooSmall > 0) {
203+
warningLabel.setText(String.format(
204+
"Warning: %d function(s) have a body of only 1 byte and cannot be selected. " +
205+
"This may indicate issues during auto-analysis.", tooSmall));
206+
warningLabel.setVisible(true);
207+
} else {
208+
warningLabel.setVisible(false);
209+
}
185210
}
186211
}

src/main/java/ai/reveng/toolkit/ghidra/binarysimilarity/ui/functionselection/FunctionSelectionTableModel.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class FunctionSelectionTableModel extends ThreadedTableModelStub<Function
2828
static final int SELECT = 0;
2929

3030
private final List<FunctionRowObject> functionList = new ArrayList<>();
31+
private int tooSmallCount = 0;
3132

3233
public FunctionSelectionTableModel(ServiceProvider serviceProvider) {
3334
super("Function Selection Table Model", serviceProvider);
@@ -40,13 +41,20 @@ public FunctionSelectionTableModel(ServiceProvider serviceProvider) {
4041
*/
4142
public void initForProgram(Program program) {
4243
functionList.clear();
44+
tooSmallCount = 0;
4345

4446
if (program != null) {
4547
program.getFunctionManager().getFunctions(true).forEach(function -> {
4648
if (!GhidraRevengService.isRelevantForAnalysis(function)) {
4749
return;
4850
}
49-
functionList.add(new FunctionRowObject(function, true));
51+
var row = new FunctionRowObject(function, true);
52+
// If the function has zero instructions then it's not a valid function to send
53+
if (!program.getListing().getInstructions(function.getBody(), true).hasNext()) {
54+
row.setEnabled(false);
55+
tooSmallCount++;
56+
}
57+
functionList.add(row);
5058
});
5159
}
5260
reload();
@@ -196,6 +204,13 @@ public List<FunctionRowObject> getAllRows() {
196204
return Collections.unmodifiableList(functionList);
197205
}
198206

207+
/**
208+
* Returns the count of functions that are too small (1 byte or less) to be analyzed.
209+
*/
210+
public int getTooSmallCount() {
211+
return tooSmallCount;
212+
}
213+
199214
/**
200215
* Returns the count of enabled (matched remotely) rows.
201216
*/

src/test/java/ai/reveng/AnalysisOptionsDialogTest.java

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,35 @@
3232
import org.junit.*;
3333

3434
import ghidra.program.database.ProgramBuilder;
35+
import ghidra.program.model.data.Undefined;
3536

3637
public class AnalysisOptionsDialogTest extends RevEngMockableHeadedIntegrationTest {
3738

3839
public AnalysisOptionsDialogTest() {
3940
super();
4041
}
4142

43+
/**
44+
* Adds a NOP instruction at the given address so the function isn't filtered
45+
* out by the no-instructions check in FunctionSelectionTableModel.
46+
*/
47+
private static void addInstruction(ProgramBuilder builder, String address) throws Exception {
48+
builder.setBytes(address, "90");
49+
builder.disassemble(address, 1);
50+
}
51+
4252
@Test
4353
public void testBasicOptionsDialog() throws Exception {
4454

4555
var reService = new GhidraRevengService( new MockApi() {});
4656
var builder = new ProgramBuilder("mock", ProgramBuilder._X64, this);
4757
// Add some functions to the program so the function selection panel has data
4858
builder.createMemory(".text", "0x401000", 0x1000);
49-
builder.createFunction("0x401000");
50-
builder.createFunction("0x401100");
59+
builder.createEmptyFunction("func1", "0x401000", 100, Undefined.getUndefinedDataType(4));
60+
builder.createEmptyFunction("func2", "0x401100", 100, Undefined.getUndefinedDataType(4));
61+
// Add instruction bytes so the functions aren't filtered out by the no-instructions check
62+
addInstruction(builder, "0x401000");
63+
addInstruction(builder, "0x401100");
5164

5265
var program = builder.getProgram();
5366
var tool = env.getTool();
@@ -83,9 +96,12 @@ public void testDialogHasFunctionSelectionPanel() throws Exception {
8396
var reService = new GhidraRevengService(new MockApi() {});
8497
var builder = new ProgramBuilder("mock", ProgramBuilder._X64, this);
8598
builder.createMemory(".text", "0x401000", 0x1000);
86-
builder.createFunction("0x401000");
87-
builder.createFunction("0x401100");
88-
builder.createFunction("0x401200");
99+
builder.createEmptyFunction("func1", "0x401000", 100, Undefined.getUndefinedDataType(4));
100+
builder.createEmptyFunction("func2", "0x401100", 100, Undefined.getUndefinedDataType(4));
101+
builder.createEmptyFunction("func3", "0x401200", 100, Undefined.getUndefinedDataType(4));
102+
addInstruction(builder, "0x401000");
103+
addInstruction(builder, "0x401100");
104+
addInstruction(builder, "0x401200");
89105

90106
var program = builder.getProgram();
91107
var tool = env.getTool();
@@ -112,8 +128,10 @@ public void testFunctionSelectionDefaultsToNonExternalNonThunk() throws Exceptio
112128
var reService = new GhidraRevengService(new MockApi() {});
113129
var builder = new ProgramBuilder("mock", ProgramBuilder._X64, this);
114130
builder.createMemory(".text", "0x401000", 0x1000);
115-
builder.createFunction("0x401000");
116-
builder.createFunction("0x401100");
131+
builder.createEmptyFunction("func1", "0x401000", 100, Undefined.getUndefinedDataType(4));
132+
builder.createEmptyFunction("func2", "0x401100", 100, Undefined.getUndefinedDataType(4));
133+
addInstruction(builder, "0x401000");
134+
addInstruction(builder, "0x401100");
117135
// Create external function (extAddress, libName, functionName)
118136
builder.createExternalFunction(null, "EXTERNAL", "printf");
119137

@@ -151,9 +169,12 @@ public void testSelectAllButtonWorksInDialog() throws Exception {
151169
var reService = new GhidraRevengService(new MockApi() {});
152170
var builder = new ProgramBuilder("mock", ProgramBuilder._X64, this);
153171
builder.createMemory(".text", "0x401000", 0x1000);
154-
builder.createFunction("0x401000");
155-
builder.createFunction("0x401100");
156-
builder.createFunction("0x401200");
172+
builder.createEmptyFunction("func1", "0x401000", 100, Undefined.getUndefinedDataType(4));
173+
builder.createEmptyFunction("func2", "0x401100", 100, Undefined.getUndefinedDataType(4));
174+
builder.createEmptyFunction("func3", "0x401200", 100, Undefined.getUndefinedDataType(4));
175+
addInstruction(builder, "0x401000");
176+
addInstruction(builder, "0x401100");
177+
addInstruction(builder, "0x401200");
157178

158179
var program = builder.getProgram();
159180
var tool = env.getTool();
@@ -194,8 +215,10 @@ public void testDeselectAllButtonWorksInDialog() throws Exception {
194215
var reService = new GhidraRevengService(new MockApi() {});
195216
var builder = new ProgramBuilder("mock", ProgramBuilder._X64, this);
196217
builder.createMemory(".text", "0x401000", 0x1000);
197-
builder.createFunction("0x401000");
198-
builder.createFunction("0x401100");
218+
builder.createEmptyFunction("func1", "0x401000", 100, Undefined.getUndefinedDataType(4));
219+
builder.createEmptyFunction("func2", "0x401100", 100, Undefined.getUndefinedDataType(4));
220+
addInstruction(builder, "0x401000");
221+
addInstruction(builder, "0x401100");
199222

200223
var program = builder.getProgram();
201224
var tool = env.getTool();
@@ -235,9 +258,12 @@ public void testGetOptionsFromUIIncludesSelectedFunctions() throws Exception {
235258
var reService = new GhidraRevengService(new MockApi() {});
236259
var builder = new ProgramBuilder("mock", ProgramBuilder._X64, this);
237260
builder.createMemory(".text", "0x401000", 0x1000);
238-
builder.createFunction("0x401000");
239-
builder.createFunction("0x401100");
240-
builder.createFunction("0x401200");
261+
builder.createEmptyFunction("func1", "0x401000", 100, Undefined.getUndefinedDataType(4));
262+
builder.createEmptyFunction("func2", "0x401100", 100, Undefined.getUndefinedDataType(4));
263+
builder.createEmptyFunction("func3", "0x401200", 100, Undefined.getUndefinedDataType(4));
264+
addInstruction(builder, "0x401000");
265+
addInstruction(builder, "0x401100");
266+
addInstruction(builder, "0x401200");
241267

242268
var program = builder.getProgram();
243269
var tool = env.getTool();

0 commit comments

Comments
 (0)