diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/FindNoReturnFunctionsAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/FindNoReturnFunctionsAnalyzer.java index b2af9b487a..f0840399ca 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/FindNoReturnFunctionsAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/FindNoReturnFunctionsAnalyzer.java @@ -89,7 +89,7 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer { public FindNoReturnFunctionsAnalyzer(String name, String description, AnalyzerType analyzerType) { super(name, description, analyzerType); - setPriority(AnalysisPriority.DISASSEMBLY.after()); + setPriority(AnalysisPriority.DISASSEMBLY.after().after()); setSupportsOneTimeAnalysis(); } @@ -373,6 +373,13 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer { } for (Address target : flows) { + // Skip targets that have a callfixup with fall-through semantics. + // A fall-through callfixup explicitly models the function's return + // behavior, so heuristic evidence should not override it. + if (hasCallFixupWithFallThrough(cp, target)) { + continue; + } + int count = 1; ReferenceIterator refsTo = cp.getReferenceManager().getReferencesTo(target); for (Reference reference : refsTo) { @@ -421,6 +428,30 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer { return hadSuspiciousFunctions; } + /** + * Check if the function at the given address has a callfixup that falls through. + * A fall-through callfixup explicitly models the function's return behavior + * (e.g., Watcom's __CHK/__STK stack-check functions), so heuristic noreturn + * detection should not override it. + * + * @param cp current program + * @param target address of the potential noreturn function + * @return true if the function has a callfixup with fall-through semantics + */ + private boolean hasCallFixupWithFallThrough(Program cp, Address target) { + Function func = cp.getFunctionManager().getFunctionAt(target); + if (func == null) { + return false; + } + String callFixup = func.getCallFixup(); + if (callFixup == null || callFixup.isEmpty()) { + return false; + } + PcodeInjectLibrary lib = cp.getCompilerSpec().getPcodeInjectLibrary(); + InjectPayload payload = lib.getPayload(InjectPayload.CALLFIXUP_TYPE, callFixup); + return payload != null && payload.isFallThru(); + } + private boolean targetOnlyCallsNoReturn(Program cp, Address target, AddressSet noReturnSet) throws CancelledException { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/CallFixupAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/CallFixupAnalyzer.java index 0d97c586ff..8dd9fbdb8e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/CallFixupAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/disassembler/CallFixupAnalyzer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -47,7 +47,7 @@ public class CallFixupAnalyzer extends AbstractAnalyzer { public CallFixupAnalyzer(String name, AnalyzerType analyzerType, boolean supportsOneTimeAnalysis) { super(name, DESCRIPTION, analyzerType); - setPriority(AnalysisPriority.DISASSEMBLY.after().after()); + setPriority(AnalysisPriority.DISASSEMBLY.after()); setDefaultEnablement(true); if (supportsOneTimeAnalysis) { setSupportsOneTimeAnalysis();