RAP-optimizations/src/size_overflow_plugin/insert_size_overflow_asm.c
2018-08-20 04:10:13 +08:00

375 lines
10 KiB
C

/*
* Copyright 2011-2017 by Emese Revfy <re.emese@gmail.com>
* Licensed under the GPL v2
*
* Homepage:
* https://github.com/ephox-gcc-plugins/size_overflow
*
* Documentation:
* http://forums.grsecurity.net/viewtopic.php?f=7&t=3043
*
* This plugin recomputes expressions of function arguments marked by a size_overflow attribute
* with double integer precision (DImode/TImode for 32/64 bit integer types).
* The recomputed argument is checked against TYPE_MAX and an event is logged on overflow and the triggering process is killed.
*
* Usage:
* $ make
* $ make run
*/
#include "size_overflow.h"
// Data for the size_overflow asm stmt
struct asm_data {
// call or return stmt with our attributes
gimple target_stmt;
// def_stmt of the marked arg
gimple def_stmt;
// size_overflow asm rhs
tree input;
// the output (lhs) of the size_overflow asm is the marked arg
tree output;
// marked arg number (0 for return values)
unsigned int argnum;
// intentional mark type
enum intentional_mark intentional_mark;
};
static void __unused print_asm_data(struct asm_data *asm_data)
{
fprintf(stderr, "-----------------------\nprint_asm_data:\n");
fprintf(stderr, "def_stmt\n");
debug_gimple_stmt(asm_data->def_stmt);
fprintf(stderr, "target_stmt\n");
debug_gimple_stmt(asm_data->target_stmt);
fprintf(stderr, "output\n");
debug_tree(asm_data->output);
fprintf(stderr, "input\n");
debug_tree(asm_data->input);
}
static const char *convert_mark_to_str(enum intentional_mark mark)
{
switch (mark) {
case MARK_NO:
return OK_ASM_STR;
case MARK_YES:
return YES_ASM_STR;
case MARK_END_INTENTIONAL:
return END_INTENTIONAL_ASM_STR;
case MARK_TURN_OFF:
return TURN_OFF_ASM_STR;
}
gcc_unreachable();
}
static char *create_asm_comment(struct asm_data *asm_data, const char *mark_str)
{
const char *fn_name;
char *asm_comment;
unsigned int len;
if (gimple_code(asm_data->target_stmt) == GIMPLE_RETURN)
fn_name = DECL_NAME_POINTER(current_function_decl);
else
fn_name = DECL_NAME_POINTER(gimple_call_fndecl(asm_data->target_stmt));
len = asprintf(&asm_comment, "%s %s %u", mark_str, fn_name, asm_data->argnum);
gcc_assert(len > 0);
return asm_comment;
}
#if BUILDING_GCC_VERSION <= 4007
static VEC(tree, gc) *create_asm_io_list(tree string, tree io)
#else
static vec<tree, va_gc> *create_asm_io_list(tree string, tree io)
#endif
{
tree list;
#if BUILDING_GCC_VERSION <= 4007
VEC(tree, gc) *vec_list = NULL;
#else
vec<tree, va_gc> *vec_list = NULL;
#endif
list = build_tree_list(NULL_TREE, string);
list = chainon(NULL_TREE, build_tree_list(list, io));
#if BUILDING_GCC_VERSION <= 4007
VEC_safe_push(tree, gc, vec_list, list);
#else
vec_safe_push(vec_list, list);
#endif
return vec_list;
}
static void create_so_asm_stmt(struct asm_data *asm_data)
{
char *asm_comment;
const char *mark_str;
gasm *asm_stmt;
gimple_stmt_iterator gsi;
tree str_input, str_output;
#if BUILDING_GCC_VERSION <= 4007
VEC(tree, gc) *input = NULL, *output = NULL;
#else
vec<tree, va_gc> *input = NULL, *output = NULL;
#endif
mark_str = convert_mark_to_str(asm_data->intentional_mark);
asm_comment = create_asm_comment(asm_data, mark_str);
str_input = build_const_char_string(2, "0");
input = create_asm_io_list(str_input, asm_data->input);
str_output = build_const_char_string(4, "=rm");
output = create_asm_io_list(str_output, asm_data->output);
asm_stmt = as_a_gasm(gimple_build_asm_vec(asm_comment, input, output, NULL, NULL));
gimple_asm_set_volatile(asm_stmt, true);
gsi = gsi_for_stmt(asm_data->def_stmt);
gsi_insert_after(&gsi, asm_stmt, GSI_NEW_STMT);
SSA_NAME_DEF_STMT(asm_data->output) = asm_stmt;
free(asm_comment);
}
static void check_size_overflow_asm(struct asm_data *asm_data)
{
enum intentional_mark old_intentional_mark = get_so_asm_type(asm_data->def_stmt);
if (old_intentional_mark == asm_data->intentional_mark)
return;
if (asm_data->intentional_mark == MARK_NO)
return;
print_intentional_mark(old_intentional_mark);
print_intentional_mark(asm_data->intentional_mark);
gcc_unreachable();
}
static tree get_so_asm_output(struct asm_data *asm_data)
{
gimple stmt = asm_data->target_stmt;
unsigned int argnum = asm_data->argnum;
switch (gimple_code(stmt)) {
case GIMPLE_RETURN:
gcc_assert(argnum == 0);
return gimple_return_retval(as_a_greturn(stmt));
case GIMPLE_CALL:
gcc_assert(argnum != 0);
gcc_assert(gimple_call_num_args(stmt) >= argnum);
return gimple_call_arg(stmt, argnum - 1);
default:
debug_gimple_stmt(stmt);
gcc_unreachable();
}
}
static tree get_so_asm_input(struct asm_data *asm_data)
{
gassign *assign;
tree output_type, new_var;
gimple_stmt_iterator gsi;
output_type = TREE_TYPE(asm_data->output);
new_var = create_new_var(output_type);
assign = gimple_build_assign(new_var, asm_data->output);
gimple_assign_set_lhs(assign, make_ssa_name(new_var, assign));
gsi = gsi_for_stmt(asm_data->target_stmt);
gsi_insert_before(&gsi, assign, GSI_NEW_STMT);
asm_data->def_stmt = assign;
new_var = create_new_var(output_type);
asm_data->output = make_ssa_name(new_var, asm_data->target_stmt);
return gimple_assign_lhs(assign);
}
static void set_so_asm_input_target_stmt(struct asm_data *asm_data)
{
switch (gimple_code(asm_data->target_stmt)) {
case GIMPLE_CALL:
gimple_call_set_arg(asm_data->target_stmt, asm_data->argnum - 1, asm_data->output);
break;
case GIMPLE_RETURN:
gimple_return_set_retval(as_a_greturn(asm_data->target_stmt), asm_data->output);
break;
default:
debug_gimple_stmt(asm_data->target_stmt);
gcc_unreachable();
}
update_stmt(asm_data->def_stmt);
}
/* This is the gimple part of searching for a missing size_overflow attribute. If the intentional_overflow attribute type
* is of the right kind create the appropriate size_overflow asm stmts:
* __asm__("# size_overflow MARK_END_INTENTIONAL" : =rm" D.3344_8 : "0" cicus.4_16);
* __asm__("# size_overflow MARK_NO" : =rm" cicus.4_16 : "0" size_1(D));
*/
static void __insert_size_overflow_asm(gimple stmt, unsigned int argnum, enum intentional_mark intentional_mark)
{
struct asm_data asm_data;
asm_data.target_stmt = stmt;
asm_data.argnum = argnum;
asm_data.intentional_mark = intentional_mark;
asm_data.output = get_so_asm_output(&asm_data);
if (asm_data.output == NULL_TREE)
return;
if (is_gimple_constant(asm_data.output))
return;
if (skip_types(asm_data.output))
return;
asm_data.def_stmt = get_def_stmt(asm_data.output);
if (is_size_overflow_asm(asm_data.def_stmt)) {
check_size_overflow_asm(&asm_data);
return;
}
asm_data.input = get_so_asm_input(&asm_data);
create_so_asm_stmt(&asm_data);
set_so_asm_input_target_stmt(&asm_data);
update_stmt(asm_data.def_stmt);
update_stmt(asm_data.target_stmt);
}
// Determine the correct arg index and arg and insert the asm stmt to mark the stmt.
static void insert_so_asm_by_so_attr(gimple stmt, unsigned int orig_argnum)
{
if (orig_argnum == 0 && gimple_code(stmt) == GIMPLE_RETURN) {
__insert_size_overflow_asm(stmt, 0, MARK_NO);
return;
}
if (orig_argnum != 0 && gimple_code(stmt) == GIMPLE_CALL)
__insert_size_overflow_asm(stmt, orig_argnum, MARK_NO);
}
// If a function arg or the return value is marked by the size_overflow attribute then set its index in the array.
static void set_argnum_attribute(const_tree attr, bool *argnums)
{
unsigned int argnum;
tree attr_value;
gcc_assert(attr);
for (attr_value = TREE_VALUE(attr); attr_value; attr_value = TREE_CHAIN(attr_value)) {
argnum = (unsigned int)tree_to_uhwi(TREE_VALUE(attr_value));
argnums[argnum] = true;
}
}
// Check whether the arguments are marked by the size_overflow attribute.
static void search_interesting_so_args(tree fndecl, bool *argnums)
{
const_tree attr;
attr = get_attribute("size_overflow", fndecl);
if (attr)
set_argnum_attribute(attr, argnums);
}
static enum intentional_mark handle_intentional_attr(gimple stmt, unsigned int argnum)
{
enum intentional_mark mark;
struct fn_raw_data raw_data;
mark = check_intentional_attribute(stmt, argnum);
if (mark == MARK_NO)
return MARK_NO;
initialize_raw_data(&raw_data);
raw_data.num = argnum;
if (gimple_code(stmt) == GIMPLE_RETURN)
raw_data.decl = current_function_decl;
else
raw_data.decl = gimple_call_fndecl(stmt);
if (raw_data.decl == NULL_TREE && !get_size_overflow_hash_entry_tree(&raw_data, DISABLE_SIZE_OVERFLOW))
return MARK_NO;
__insert_size_overflow_asm(stmt, argnum, mark);
return mark;
}
static void handle_size_overflow_attr_ret(greturn *stmt)
{
enum intentional_mark mark;
bool orig_argnums[MAX_PARAM + 1] = {false};
search_interesting_so_args(get_orig_fndecl(current_function_decl), (bool *) &orig_argnums);
mark = handle_intentional_attr(stmt, 0);
if (mark == MARK_NO && orig_argnums[0])
insert_so_asm_by_so_attr(stmt, 0);
}
// If the argument(s) of the callee function are marked by an attribute then mark the call stmt with an asm stmt
static void handle_size_overflow_attr_call(gcall *stmt)
{
tree fndecl;
unsigned int argnum;
bool orig_argnums[MAX_PARAM + 1] = {false};
fndecl = get_interesting_orig_fndecl_from_stmt(stmt);
if (fndecl == NULL_TREE)
return;
if (DECL_BUILT_IN(fndecl))
return;
search_interesting_so_args(fndecl, (bool *) &orig_argnums);
for (argnum = 1; argnum <= gimple_call_num_args(stmt); argnum++) {
enum intentional_mark mark = handle_intentional_attr(stmt, argnum);
if (mark == MARK_NO && !is_vararg(fndecl, argnum) && orig_argnums[argnum])
insert_so_asm_by_so_attr(stmt, argnum);
}
}
// Iterate over all the stmts and search for call stmts and mark them if they have size_overflow attribute
static unsigned int insert_size_overflow_asm_execute(void)
{
basic_block bb;
FOR_EACH_BB_FN(bb, cfun) {
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
gimple stmt = gsi_stmt(gsi);
if (is_gimple_call(stmt))
handle_size_overflow_attr_call(as_a_gcall(stmt));
else if (gimple_code(stmt) == GIMPLE_RETURN)
handle_size_overflow_attr_ret(as_a_greturn(stmt));
}
}
return 0;
}
/*
* A lot of functions get inlined before the ipa passes so after the build_ssa gimple pass
* this pass inserts asm stmts to mark the interesting args
* that the ipa pass will detect and insert the size overflow checks for.
*/
#define PASS_NAME insert_size_overflow_asm
#define NO_GATE
#define PROPERTIES_REQUIRED PROP_cfg
#define TODO_FLAGS_FINISH TODO_dump_func | TODO_verify_ssa | TODO_verify_stmts | TODO_remove_unused_locals | TODO_update_ssa_no_phi | TODO_cleanup_cfg | TODO_ggc_collect | TODO_verify_flow
#include "gcc-generate-gimple-pass.h"