HPCToolkit
x86-validate-retn-addr.c
Go to the documentation of this file.
1 // -*-Mode: C++;-*- // technically C99
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are
20 // met:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 //
48 // Validate a return address obtained from an hpcrun_unw_step:
49 // Most useful after a troll operation, but may still be used for
50 // QC on all unwinds
51 //
52 
53 //***************************************************************************
54 // system include files
55 //***************************************************************************
56 
57 #include <stdbool.h>
58 
59 
60 
61 //***************************************************************************
62 // local include files
63 //***************************************************************************
64 
65 #include "x86-decoder.h"
66 #include "x86-validate-retn-addr.h"
67 #include "x86-unwind-analysis.h"
68 #include "fnbounds_interface.h"
69 #include "validate_return_addr.h"
70 #include "uw_recipe_map.h"
71 #include "x86-unwind-interval.h"
72 
74 #include <messages/messages.h>
75 
77 
78 
79 //****************************************************************************
80 // local types
81 //****************************************************************************
82 
83 typedef struct xed_decode_t {
84  xed_decoded_inst_t xedd;
85  xed_error_enum_t err;
86 } xed_decode_t;
87 
88 
89 
90 //****************************************************************************
91 // local operations
92 //****************************************************************************
93 
94 // this function provides a convenient single breakpoint location for stopping
95 // when an invalid unwind is found.
96 static validation_status
98 {
99  return UNW_ADDR_WRONG;
100 }
101 
102 
103 static void
104 xed_decode_i(void *ins, xed_decode_t *res)
105 {
106  xed_decoded_inst_t *xptr = &(res->xedd);
107  xed_decoded_inst_zero_set_mode(xptr, &x86_decoder_settings.xed_settings);
108  xed_decoded_inst_zero_keep_mode(xptr);
109  res->err = xed_decode(xptr, (uint8_t *)ins, 15);
110 }
111 
112 
113 static bool
114 confirm_call_fetch_addr(void *addr, size_t offset, void **the_call)
115 {
116  xed_decode_t xed;
117 
118  void *possible_call = addr - offset;
119 
120  *the_call = NULL;
121  xed_decode_i(possible_call, &xed);
122 
123  if (xed.err != XED_ERROR_NONE) {
124  return false;
125  }
126  xed_decoded_inst_t *xptr = &xed.xedd;
127  if ( xed_decoded_inst_get_length(xptr) != offset ){
128  TMSG(VALIDATE_UNW,"instruction @ offset %d from %p does not have length corresponding to the offset",
129  offset, addr);
130  return false;
131  }
132  switch(xed_decoded_inst_get_iclass(xptr)) {
133  case XED_ICLASS_CALL_FAR:
134  case XED_ICLASS_CALL_NEAR:
135  TMSG(VALIDATE_UNW,"call instruction confirmed @ %p", possible_call);
136  *the_call = x86_get_branch_target(possible_call, xptr);
137  return true;
138  break;
139  default:
140  return false;
141  }
142  EMSG("MAJOR PROGRAMMING ERROR: impossible fall thru @confirm_call_fetch_addr");
143  return false;
144 }
145 
146 
147 static bool
148 confirm_call(void *addr, void *routine)
149 {
150  TMSG(VALIDATE_UNW,"Checking for true call immediately preceding %p",addr);
151 
152  void *the_call;
153 
154  if ( ! confirm_call_fetch_addr(addr, 5, &the_call)) {
155  TMSG(VALIDATE_UNW,"No true call instruction found, so true call REJECTED");
156  return false;
157  }
158  TMSG(VALIDATE_UNW,"comparing called routine %p to actual routine %p",
159  the_call, routine);
160  return (the_call == routine);
161 }
162 
163 
164 static bool
165 confirm_indirect_call_specific(void* addr, size_t offset, void** call_ins)
166 {
167  void *callee;
168  if ( ! confirm_call_fetch_addr(addr, offset, &callee)) {
169  TMSG(VALIDATE_UNW,"No call instruction found @ %p, so indirect call at this location rejected",
170  addr - offset);
171  return false;
172  }
173  if (callee == NULL) {
174  *call_ins = addr - offset;
175  }
176  return (callee == NULL);
177 }
178 
179 static bool
180 confirm_indirect_call(void* addr, void** call_ins)
181 {
182  TMSG(VALIDATE_UNW,"trying to confirm an indirect call preceeding %p", addr);
183  for (size_t i=1;i <= 7;i++) {
184  if (confirm_indirect_call_specific(addr, i, call_ins)){
185  return true;
186  }
187  }
188  return false;
189 }
190 
191 static validation_status
192 contains_tail_call_to_f(void *callee, void *target_fn)
193 {
194  void *routine_start, *routine_end;
195  if (! fnbounds_enclosing_addr(callee, &routine_start, &routine_end, NULL)) {
196  TMSG(VALIDATE_UNW,"unwind addr %p does NOT have function bounds, so it is invalid",callee);
197  return status_is_wrong(); // hard error: callee is nonsense
198  }
199 
200  xed_decoded_inst_t xedd;
201  xed_decoded_inst_t *xptr = &xedd;
202  xed_error_enum_t xed_error;
203  validation_status status = UNW_ADDR_WRONG;
204 
205  xed_decoded_inst_zero_set_mode(xptr, &x86_decoder_settings.xed_settings);
206 
207  void *ins = routine_start;
208  while (ins < routine_end) {
209  xed_decoded_inst_zero_keep_mode(xptr);
210  xed_error = xed_decode(xptr, (uint8_t*) ins, 15);
211 
212  if (xed_error != XED_ERROR_NONE) {
213  ins++; /* skip this byte */
214  continue; /* continue onward ... */
215  }
216 
217  xed_iclass_enum_t xiclass = xed_decoded_inst_get_iclass(xptr);
218 
219  switch(xiclass) {
220  // branches
221  case XED_ICLASS_JBE:
222  case XED_ICLASS_JL:
223  case XED_ICLASS_JLE:
224  case XED_ICLASS_JNB:
225  case XED_ICLASS_JNBE:
226  case XED_ICLASS_JNL:
227  case XED_ICLASS_JNLE:
228  case XED_ICLASS_JNO:
229  case XED_ICLASS_JNP:
230  case XED_ICLASS_JNS:
231  case XED_ICLASS_JNZ:
232  case XED_ICLASS_JO:
233  case XED_ICLASS_JP:
234  case XED_ICLASS_JRCXZ:
235  case XED_ICLASS_JS:
236  case XED_ICLASS_JZ:
237  // jumps
238  case XED_ICLASS_JMP:
239  case XED_ICLASS_JMP_FAR:
240  {
241  void *target = x86_get_branch_target(ins, xptr);
242  if ((target >= routine_end) || (target < routine_start)) {
243  // tail call
244  if (target == target_fn) return UNW_ADDR_CONFIRMED;
245  status = UNW_ADDR_PROBABLE_TAIL;
246  }
247  }
248  break;
249  default:
250  break;
251  }
252  ins = ins + xed_decoded_inst_get_length(xptr);
253  }
254  return status;
255 }
256 
257 
258 static validation_status
259 confirm_tail_call(void *addr, void *target_fn)
260 {
261  TMSG(VALIDATE_UNW,"trying to confirm that instruction before %p is call to a routine with tail calls", addr);
262 
263  void *callee;
264 
265  if ( ! confirm_call_fetch_addr(addr, 5, &callee)) {
266  TMSG(VALIDATE_UNW,"No call instruction found @ %p, so tail call REJECTED", addr - 5);
267  return UNW_ADDR_WRONG; // soft error: this case doesn't apply
268  }
269 
270  TMSG(VALIDATE_UNW,"Checking routine %p for possible tail calls", callee);
271 
272  unwindr_info_t unwr_info;
273  bool found = uw_recipe_map_lookup(callee, NATIVE_UNWINDER, &unwr_info);
274  if (found && (unwr_info.treestat == READY)
275  && UWI_RECIPE(unwr_info.btuwi)->has_tail_calls)
276  return contains_tail_call_to_f(callee, target_fn);
277 
278  return status_is_wrong();
279 }
280 
281 
282 static void *
283 x86_plt_branch_target(void *ins, xed_decoded_inst_t *xptr)
284 {
285  const xed_inst_t *xi = xed_decoded_inst_inst(xptr);
286  const xed_operand_t *op0 = xed_inst_operand(xi, 0);
287  xed_operand_enum_t op0_name = xed_operand_name(op0);
288  xed_operand_type_enum_t op0_type = xed_operand_type(op0);
289 
290  if (op0_name == XED_OPERAND_MEM0 &&
291  op0_type == XED_OPERAND_TYPE_IMM_CONST) {
292  xed_operand_values_t *vals = xed_decoded_inst_operands(xptr);
293  xed_reg_enum_t reg = xed_operand_values_get_base_reg(vals,0);
294  if (x86_isReg_IP(reg)) {
295  int ins_len = xed_decoded_inst_get_length(xptr);
296  xed_int64_t disp = xed_operand_values_get_memory_displacement_int64(vals);
297  xed_int64_t **memloc = ins + disp + ins_len;
298  xed_int64_t *memval = *memloc;
299  return memval;
300  }
301  }
302  return NULL;
303 }
304 
305 
306 static
308 confirm_plt_call(void *addr, void *callee)
309 {
310 
311  TMSG(VALIDATE_UNW,"trying to confirm that instruction before %p is call to a routine through the PLT", addr);
312 
313  void *plt_ins;
314 
315  if ( ! confirm_call_fetch_addr(addr, 5, &plt_ins)) {
316  TMSG(VALIDATE_UNW,"No call instruction found @ %p, so PLT call REJECTED",
317  addr - 5);
318  return UNW_ADDR_WRONG;
319  }
320 
321  TMSG(VALIDATE_UNW,"Checking at %p for PLT call", plt_ins);
322 
323  xed_decode_t xed;
324  xed_decode_i(plt_ins, &xed);
325  xed_decoded_inst_t *xptr = &xed.xedd;
326 
327  void *plt_callee = x86_plt_branch_target(plt_ins, xptr);
328  if (plt_callee == callee) return UNW_ADDR_CONFIRMED;
329 
330 #if 0
331  unwind_interval *plt_callee_ui;
332  bool found = uw_recipe_map_lookup(plt_callee, NATIVE_UNWINDER, NULL, &plt_callee_ui);
333  if (found && UWI_RECIPE(plt_callee_ui)->has_tail_calls) return contains_tail_call_to_f(plt_callee, callee);
334 #else
335 
336  unwindr_info_t unwr_info;
337  bool found = uw_recipe_map_lookup(plt_callee, NATIVE_UNWINDER, &unwr_info);
338  if (found && (unwr_info.treestat == READY)
339  && UWI_RECIPE(unwr_info.btuwi)->has_tail_calls)
340  return contains_tail_call_to_f(plt_callee, callee);
341 
342 #endif
343 
344  return UNW_ADDR_WRONG;
345 }
346 
347 static int
349 {
350  return (uw_recipe_map_lookup(addr, NATIVE_UNWINDER, unwr_info) &&
351  unwr_info->treestat != NEVER);
352 }
353 
354 //****************************************************************************
355 // interface operations
356 //****************************************************************************
358 deep_validate_return_addr(void* addr, void* generic)
359 {
360  hpcrun_unw_cursor_t* cursor = (hpcrun_unw_cursor_t*) generic;
361 
362  TMSG(VALIDATE_UNW,"validating unwind step from %p ==> %p",cursor->pc_unnorm,
363  addr);
364 
365  unwindr_info_t unwr_info;
366  if (!return_addr_valid(addr, &unwr_info) ) {
367  TMSG(VALIDATE_UNW,"unwind addr %p does NOT have function bounds, so it is invalid", addr);
368  return status_is_wrong();
369  }
370 
371  if (!return_addr_valid(cursor->pc_unnorm, &unwr_info))
372  return status_is_wrong();
373 
374  void* callee = (void*)unwr_info.interval.start;
375  TMSG(VALIDATE_UNW, "beginning of my routine = %p", callee);
376  if (confirm_call(addr, callee)) {
377  TMSG(VALIDATE_UNW, "Instruction preceeding %p is a call to this routine. Unwind confirmed", addr);
378  return UNW_ADDR_CONFIRMED;
379  }
380  validation_status result = confirm_plt_call(addr, callee);
381  if (result != UNW_ADDR_WRONG) {
382  TMSG(VALIDATE_UNW,
383  "Instruction preceeding %p is a call through the PLT to this routine. Unwind confirmed",
384  addr);
385  return result;
386  }
387  result = confirm_tail_call(addr, callee);
388  if (result != UNW_ADDR_WRONG) {
389  TMSG(VALIDATE_UNW,"Instruction preceeding %p is a call to a routine that has tail calls. Unwind is LIKELY ok", addr);
390  return result;
391  }
392 
393  void* call_ins;
394  if (confirm_indirect_call(addr, &call_ins)){
395  TMSG(VALIDATE_UNW,"Instruction preceeding %p is an indirect call. Unwind is LIKELY ok", addr);
396  return UNW_ADDR_PROBABLE_INDIRECT;
397  }
398  TMSG(VALIDATE_UNW,"Unwind addr %p is NOT confirmed", addr);
399  return status_is_wrong();
400 }
401 
402 
404 dbg_val(void *addr, void *pc)
405 {
406  hpcrun_unw_cursor_t cursor;
407 
408  cursor.pc_unnorm = pc;
409  return deep_validate_return_addr(addr, &cursor);
410 }
411 
412 
414 validate_return_addr(void *addr, void *generic)
415 {
416  unwindr_info_t unwr_info;
417  return return_addr_valid(addr, &unwr_info) ?
418  UNW_ADDR_PROBABLE : UNW_ADDR_WRONG;
419 }
static validation_status status_is_wrong()
xed_state_t xed_settings
Definition: x86-decoder.h:61
static bool confirm_indirect_call(void *addr, void **call_ins)
struct xed_decode_t xed_decode_t
validation_status
validation_status validate_return_addr(void *addr, void *generic)
xed_error_enum_t err
static size_t ins_len(void *seq)
Definition: amd-xop.c:202
static bool confirm_indirect_call_specific(void *addr, size_t offset, void **call_ins)
interval_t interval
Definition: unwindr_info.h:100
static int return_addr_valid(void *addr, unwindr_info_t *unwr_info)
static void xed_decode_i(void *ins, xed_decode_t *res)
#define UWI_RECIPE(btuwi)
bitree_uwi_t * btuwi
Definition: unwindr_info.h:103
bool uw_recipe_map_lookup(void *addr, unwinder_t uw, unwindr_info_t *unwr_info)
static bool confirm_call(void *addr, void *routine)
validation_status dbg_val(void *addr, void *pc)
static bool confirm_call_fetch_addr(void *addr, size_t offset, void **the_call)
#define EMSG
Definition: messages.h:70
xed_control_t x86_decoder_settings
Definition: x86-decoder.c:58
void * x86_get_branch_target(void *ins, xed_decoded_inst_t *xptr)
xed_decoded_inst_t xedd
static validation_status confirm_tail_call(void *addr, void *target_fn)
bool found
Definition: cct.c:129
#define TMSG(f,...)
Definition: messages.h:93
static validation_status contains_tail_call_to_f(void *callee, void *target_fn)
#define NULL
Definition: ElfHelper.cpp:85
static void * x86_plt_branch_target(void *ins, xed_decoded_inst_t *xptr)
bitree_uwi_t unwind_interval
uintptr_t start
Definition: interval_t.h:27
static validation_status confirm_plt_call(void *addr, void *callee)
cct_addr_t * addr
Definition: cct.c:130
tree_stat_t treestat
Definition: unwindr_info.h:102
validation_status deep_validate_return_addr(void *addr, void *generic)
bool fnbounds_enclosing_addr(void *ip, void **start, void **end, load_module_t **lm)