@@ -128,32 +128,48 @@ no_return_block(Block,"no-fallthrough"):-
128128 no_return_call_propagated(BlockEnd,_)
129129 ),
130130 block_boundaries(Block,Beg,EndBlock),
131- EndBlock <= End,
132- !plt_block(Block,_).
133-
131+ EndBlock <= End.
134132
135133/**
136- A function `Func` is called at `Call`, and the call falls through
137- interprocedurally.
138- There may be NOP paddings after the function call.
134+ A function `Func` is called at instruction `Call` and the call may fall through
135+ to the next instruction interprocedurally.
136+ NOP or padding instructions may follow the call.
139137*/
140- .decl call_may_fallthrough_inter(Call:address,Func:address,From:address)
141- .output call_may_fallthrough_inter
138+ .decl call_may_fallthrough_inter(Call:address,Func:address)
142139
143- call_may_fallthrough_inter(Call,Func,From ):-
140+ call_may_fallthrough_inter(Call,Func):-
144141 direct_call(Call,Func),
145- may_fallthrough(Call,Fallthrough),
146- (
147- !candidate_block_is_padding(Fallthrough),
148- Next = Fallthrough,
149- From = Call
150- ;
151- candidate_block_is_padding(Fallthrough),
152- block_last_instruction(Fallthrough,From),
153- may_fallthrough(From,Next)
154- ),
142+ block_last_instruction(CallBlock,Call),
143+ fallthrough_over_padding(CallBlock,From,Next),
155144 inter_procedural_edge(From,Next).
156145
146+ /**
147+ Represents the next initial function following `Func`
148+ (from `function_entry_initial`, before use-def and value analysis).
149+ `NextFunc` is the immediate successor of `Func`.
150+ */
151+ .decl next_function_entry_initial(Func:address,NextFunc:address)
152+
153+ next_function_entry_initial(Func, NextFunc) :-
154+ function_inference.function_entry_initial(Func),
155+ NextFunc = min F : {
156+ function_inference.function_entry_initial(F),
157+ F > Func
158+ }.
159+
160+ /**
161+ An initial function (`function_entry_initial`, prior to use-def
162+ and value analysis) contains one or more `return` instructions.
163+ This is a heuristic and may generate false positives.
164+ */
165+ .decl initial_function_containing_return(Func:address,ReturnEA:address)
166+
167+ initial_function_containing_return(Func,EA):-
168+ next_function_entry_initial(Func, NextFunc),
169+ EA >= Func,
170+ EA < NextFunc,
171+ arch.return(EA).
172+
157173/**
158174Calls to known no return functions or their PLT blocks.
159175*/
@@ -173,6 +189,17 @@ no_return_block(Block,"no_return_call_refined"):-
173189 no_return_call_refined(BlockEnd),
174190 block_last_instruction(Block,BlockEnd).
175191
192+ /**
193+ A call `Call` targets a function `CallTarget` that has another call-site
194+ which falls through interprocedurally.
195+ */
196+ .decl call_target_has_other_fallthrough_inter(Call:address,CallTarget:address)
197+
198+ call_target_has_other_fallthrough_inter(Call,CallTarget):-
199+ call_may_fallthrough_inter(OtherCall,CallTarget),
200+ direct_call(Call,CallTarget),
201+ OtherCall != Call.
202+
176203/**
177204Calls to noreturn blocks.
178205*/
@@ -192,7 +219,7 @@ no_return_call_propagated(EA,"direct_call to no_return_block"):-
192219// it's interprocedural.
193220no_return_call_propagated(Call,"interprocedural fallthrough"):-
194221 direct_call(Call,CallTarget),
195- call_may_fallthrough_inter(Call,CallTarget,_ ),
222+ call_may_fallthrough_inter(Call,CallTarget),
196223 !no_return_call_refined(Call),
197224 !pc_load_call(Call,CallTarget).
198225
@@ -204,22 +231,31 @@ no_return_call_propagated(Call,"interprocedural fallthrough"):-
204231// To avoid incorrectly removing legitimate fallthrough CFG edges, this rule
205232// conservatively checks whether `Next` is a `possible_target`, and if not,
206233// we do not classify the function call as noreturn function call.
207- no_return_call_propagated(Call,"call possiblly_no_return_func and fallthrough-next is a possible target"):-
208- direct_call(Call,CallTarget),
209- !call_may_fallthrough_inter(Call,CallTarget,_),
234+ no_return_call_propagated(
235+ Call,
236+ "call possiblly_no_return_func and fallthrough-next is a possible target"
237+ ):-
238+ call_target_has_other_fallthrough_inter(Call,CallTarget),
239+ !call_may_fallthrough_inter(Call,CallTarget),
210240 !pc_load_call(Call,CallTarget),
211241 !no_return_call_refined(Call),
212- 0 != count : {
213- call_may_fallthrough_inter(OtherCall,CallTarget,_),
214- OtherCall != Call
215- },
216- may_fallthrough(Call,Fallthrough),
242+ block_last_instruction(CallBlock,Call),
243+ fallthrough_over_padding(CallBlock,_,Next),
217244 (
218- !candidate_block_is_padding(Fallthrough),
219- Next = Fallthrough
245+ // No return instruction appears in the target function (internal).
246+ !initial_function_containing_return(CallTarget,_), UNUSED(Next),
247+ !plt_block(CallTarget,_)
220248 ;
221- candidate_block_is_padding(Fallthrough),
222- block_last_instruction(Fallthrough,From),
223- may_fallthrough(From,Next)
224- ),
225- possible_target(Next).
249+ // If the target is external, there is not enough information to
250+ // determine whether it is conditionally non-returning.
251+ // As a heuristic, classify it as a no-return call only when
252+ // Next is a possible target.
253+ plt_block(CallTarget,_),
254+ possible_target(Next)
255+ ;
256+ // The call target function may contain a return, which may
257+ // indicate that it is a conditionally non-returning function.
258+ // Further check whether Next is a possible target.
259+ initial_function_containing_return(CallTarget,_),
260+ possible_target(Next)
261+ ).
0 commit comments