RAP-optimizations/src/rap_plugin/rap_fptr_pass.c
2018-09-04 20:23:56 +08:00

298 lines
8.9 KiB
C

/*
* Copyright 2012-2017 by PaX Team <pageexec@freemail.hu>
* Licensed under the GPL v2
*
* Homepage: http://pax.grsecurity.net/
*/
#include "rap.h"
bool report_runtime;
bool report_fptr_hash;
static bool rap_fptr_gate(void)
{
return rap_cmodel_check();
}
static tree build_rap_hash(gimple call_stmt, tree fntype)
{
rap_hash_t hash;
hash = rap_hash_function_type(fntype, imprecise_rap_hash_flags);
if (report_fptr_hash)
inform(gimple_location(call_stmt), "fptr rap_hash: %x", hash.hash);
return build_int_cst_type(rap_hash_type_node, hash.hash);
}
// extract the rap_hash stored at an offset from ptr
tree get_rap_hash(gimple_seq *stmts, location_t loc, tree fptr, HOST_WIDE_INT rap_hash_offset)
{
gimple assign_hash;
tree target_hash;
#if BUILDING_GCC_VERSION == 4005
tree fptr2;
#endif
// target_hash = *(long*)((void*)fptr + rap_hash_offset)
target_hash = create_tmp_var(rap_hash_type_node, "rap_hash");
add_referenced_var(target_hash);
target_hash = make_ssa_name(target_hash, NULL);
#if BUILDING_GCC_VERSION == 4005
fptr2 = create_tmp_var(ptr_type_node, "rap_fptr2");
fptr2 = make_ssa_name(fptr2, NULL);
assign_hash = gimple_build_assign(fptr2, build2(POINTER_PLUS_EXPR, ptr_type_node, fptr, build_int_cst_type(sizetype, rap_hash_offset)));
gimple_set_location(assign_hash, loc);
SSA_NAME_DEF_STMT(fptr2) = assign_hash;
gimple_seq_add_stmt(stmts, assign_hash);
fptr = gimple_get_lhs(assign_hash);
fptr2 = create_tmp_var(build_pointer_type(rap_hash_type_node), "rap_fptr2");
fptr2 = make_ssa_name(fptr2, NULL);
assign_hash = gimple_build_assign(fptr2, fold_convert(build_pointer_type(TREE_TYPE(target_hash)), fptr));
gimple_set_location(assign_hash, loc);
SSA_NAME_DEF_STMT(fptr2) = assign_hash;
gimple_seq_add_stmt(stmts, assign_hash);
fptr = gimple_get_lhs(assign_hash);
assign_hash = gimple_build_assign(target_hash, build1(INDIRECT_REF, rap_hash_type_node, fptr));
#else
assign_hash = gimple_build_assign(target_hash, build2(MEM_REF, rap_hash_type_node, fptr, build_int_cst_type(build_pointer_type(rap_hash_type_node), rap_hash_offset)));
#endif
gimple_set_location(assign_hash, loc);
SSA_NAME_DEF_STMT(target_hash) = assign_hash;
gimple_seq_add_stmt(stmts, assign_hash);
return target_hash;
}
static void rap_mark_retloc(gimple_stmt_iterator *gsi, tree computed_hash)
{
gimple stmt;
VEC(tree, gc) *inputs = NULL;
VEC(tree, gc) *clobbers = NULL;
tree input, clobber, fndecl;
fndecl = gimple_call_fndecl(gsi_stmt(*gsi));
if (fndecl && is_simple_builtin(fndecl))
return;
input = build_tree_list(NULL_TREE, build_const_char_string(2, "i"));
input = chainon(NULL_TREE, build_tree_list(input, computed_hash));
VEC_safe_push(tree, gc, inputs, input);
clobber = build_tree_list(NULL_TREE, build_const_char_string(3, "cx"));
VEC_safe_push(tree, gc, clobbers, clobber);
stmt = gimple_build_asm_vec("", inputs, NULL, NULL, NULL);
gimple_asm_set_volatile(as_a_gasm(stmt), true);
gimple_set_location(stmt, gimple_location(gsi_stmt(*gsi)));
gsi_insert_before(gsi, stmt, GSI_SAME_STMT);
}
// check the function hash of the target of the fptr
static void rap_instrument_fptr(gimple_stmt_iterator *gsi)
{
gimple check_hash, call_stmt, stmt;
gimple_seq stmts = NULL;
location_t loc;
tree computed_hash, target_hash, fptr, fntype;
basic_block cond_bb, join_bb, true_bb;
edge e;
HOST_WIDE_INT rap_hash_offset;
call_stmt = gsi_stmt(*gsi);
loc = gimple_location(call_stmt);
fptr = gimple_call_fn(call_stmt);
fntype = TREE_TYPE(TREE_TYPE(fptr));
if (TREE_CODE(fntype) == FUNCTION_TYPE) {
computed_hash = build_rap_hash(call_stmt, fntype);
} else {
debug_tree(fntype);
gcc_unreachable();
}
/* Try rap optimization */
if (UNITS_PER_WORD == 8)
rap_hash_offset = 2 * sizeof(rap_hash_t);
else if (UNITS_PER_WORD == 4)
rap_hash_offset = sizeof(rap_hash_t);
else
gcc_unreachable();
target_hash = get_rap_hash(&stmts, loc, fptr, -rap_hash_offset);
gsi_insert_seq_before(gsi, stmts, GSI_SAME_STMT);
// compare target_hash against computed function hash
// bail out on mismatch
check_hash = gimple_build_cond(NE_EXPR, target_hash, computed_hash, NULL_TREE, NULL_TREE);
gimple_set_location(check_hash, loc);
gsi_insert_before(gsi, check_hash, GSI_NEW_STMT);
cond_bb = gimple_bb(gsi_stmt(*gsi));
gcc_assert(!gsi_end_p(*gsi));
gcc_assert(check_hash == gsi_stmt(*gsi));
e = split_block(cond_bb, gsi_stmt(*gsi));
cond_bb = e->src;
join_bb = e->dest;
e->flags = EDGE_FALSE_VALUE;
e->probability = REG_BR_PROB_BASE;
true_bb = create_empty_bb(EXIT_BLOCK_PTR_FOR_FN(cfun)->prev_bb);
make_edge(cond_bb, true_bb, EDGE_TRUE_VALUE | EDGE_PRESERVE);
gcc_assert(dom_info_available_p(CDI_DOMINATORS));
set_immediate_dominator(CDI_DOMINATORS, true_bb, cond_bb);
set_immediate_dominator(CDI_DOMINATORS, join_bb, cond_bb);
gcc_assert(cond_bb->loop_father == join_bb->loop_father);
add_bb_to_loop(true_bb, cond_bb->loop_father);
*gsi = gsi_start_bb(true_bb);
if (report_runtime) {
VEC(tree, gc) *inputs = NULL;
tree input;
// build the equivalence of asm volatile ("" : : "a"(fptr));
input = build_tree_list(NULL_TREE, build_const_char_string(2, "a"));
input = chainon(NULL_TREE, build_tree_list(input, fptr));
VEC_safe_push(tree, gc, inputs, input);
stmt = gimple_build_asm_vec("", inputs, NULL, NULL, NULL);
gimple_asm_set_volatile(as_a_gasm(stmt), true);
gimple_set_location(stmt, loc);
gsi_insert_after(gsi, stmt, GSI_CONTINUE_LINKING);
}
if (rap_abort_call) {
stmt = gimple_build_asm_vec(rap_abort_call, NULL, NULL, NULL, NULL);
gimple_asm_set_volatile(as_a_gasm(stmt), true);
gimple_set_location(stmt, loc);
gsi_insert_after(gsi, stmt, GSI_CONTINUE_LINKING);
stmt = gimple_build_call(builtin_decl_implicit(BUILT_IN_UNREACHABLE), 0);
} else {
// this fake dependency is to prevent PRE from merging this BB with others of the same kind
stmt = barrier(fptr, false);
gimple_set_location(stmt, loc);
gsi_insert_after(gsi, stmt, GSI_CONTINUE_LINKING);
stmt = gimple_build_call(builtin_decl_implicit(BUILT_IN_TRAP), 0);
}
gimple_set_location(stmt, loc);
gsi_insert_after(gsi, stmt, GSI_CONTINUE_LINKING);
*gsi = gsi_start_bb(join_bb);
}
// find all language level function pointer dereferences and verify the target function
static unsigned int rap_fptr_execute(void)
{
basic_block bb;
loop_optimizer_init(LOOPS_NORMAL | LOOPS_HAVE_RECORDED_EXITS);
gcc_assert(current_loops);
calculate_dominance_info(CDI_DOMINATORS);
calculate_dominance_info(CDI_POST_DOMINATORS);
// 1. loop through BBs and GIMPLE statements
FOR_EACH_BB_FN(bb, cfun) {
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
// gimple match: h_1 = get_fptr (); D.2709_3 = h_1 (x_2(D));
tree fptr, fntype, computed_hash;
gimple call_stmt;
rap_hash_t hash;
// is it a call ...
call_stmt = gsi_stmt(gsi);
if (!is_gimple_call(call_stmt))
continue;
fptr = gimple_call_fn(call_stmt);
if (!fptr)
continue;
switch (TREE_CODE(fptr)) {
default:
debug_gimple_stmt(call_stmt);
debug_tree(fptr);
debug_tree(TREE_TYPE(fptr));
gcc_unreachable();
case ADDR_EXPR:
hash = rap_hash_function_type(TREE_TYPE(TREE_OPERAND(fptr, 0)), imprecise_rap_hash_flags);
computed_hash = build_int_cst_type(rap_hash_type_node, -hash.hash);
rap_mark_retloc(&gsi, computed_hash);
continue;
case SSA_NAME:
if (SSA_NAME_VAR(fptr) == NULL_TREE)
break;
switch (TREE_CODE(SSA_NAME_VAR(fptr))) {
default:
debug_gimple_stmt(call_stmt);
debug_tree(fptr);
gcc_unreachable();
case VAR_DECL:
case PARM_DECL:
break;
}
break;
case INTEGER_CST:
case OBJ_TYPE_REF:
break;
}
// ... through a function pointer
fntype = TREE_TYPE(fptr);
if (TREE_CODE(fntype) != POINTER_TYPE) {
gcc_assert(TREE_CODE(fntype) == FUNCTION_TYPE || TREE_CODE(fntype) == METHOD_TYPE);
hash = rap_hash_function_type(fntype, imprecise_rap_hash_flags);
computed_hash = build_int_cst_type(rap_hash_type_node, -hash.hash);
rap_mark_retloc(&gsi, computed_hash);
continue;
}
fntype = TREE_TYPE(fntype);
gcc_assert(TREE_CODE(fntype) == FUNCTION_TYPE || TREE_CODE(fntype) == METHOD_TYPE);
if (enable_type_call) {
rap_instrument_fptr(&gsi);
bb = gsi_bb(gsi);
gcc_assert(call_stmt == gsi_stmt(gsi));
}
if (enable_type_ret) {
hash = rap_hash_function_type(fntype, imprecise_rap_hash_flags);
computed_hash = build_int_cst_type(rap_hash_type_node, -hash.hash);
rap_mark_retloc(&gsi, computed_hash);
}
bb = gsi_bb(gsi);
gcc_assert(call_stmt == gsi_stmt(gsi));
}
}
free_dominance_info(CDI_DOMINATORS);
free_dominance_info(CDI_POST_DOMINATORS);
loop_optimizer_finalize();
return 0;
}
#define PASS_NAME rap_fptr
#define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func | TODO_remove_unused_locals | TODO_update_ssa | TODO_cleanup_cfg | TODO_rebuild_cgraph_edges | TODO_verify_flow
#include "gcc-generate-gimple-pass.h"