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

506 lines
12 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"
bool is_vararg(const_tree fn, unsigned int num)
{
tree arg_list;
if (num == 0)
return false;
if (fn == NULL_TREE)
return false;
if (TREE_CODE(fn) != FUNCTION_DECL)
return false;
arg_list = TYPE_ARG_TYPES(TREE_TYPE(fn));
if (arg_list == NULL_TREE)
return false;
if (tree_last(arg_list) == void_list_node)
return false;
return num >= (unsigned int)list_length(arg_list);
}
// Extract the field decl from memory references
tree get_ref_field(const_tree ref)
{
tree field;
// TODO: handle nested memory references
switch (TREE_CODE(ref)) {
case ARRAY_REF:
return NULL_TREE;
#if BUILDING_GCC_VERSION >= 4006
case MEM_REF:
#endif
case INDIRECT_REF:
field = TREE_OPERAND(ref, 0);
break;
case COMPONENT_REF:
field = TREE_OPERAND(ref, 1);
break;
default:
return NULL_TREE;
}
// TODO
if (TREE_CODE(field) == SSA_NAME)
return NULL_TREE;
// TODO
if (TREE_CODE(field) != FIELD_DECL)
return NULL_TREE;
// TODO
if (TREE_CODE(field) == ADDR_EXPR)
return NULL_TREE;
return field;
}
const char *get_type_name_from_field(const_tree field_decl)
{
const_tree context, type_name;
if (TREE_CODE(field_decl) != FIELD_DECL)
return NULL;
context = DECL_CONTEXT(field_decl);
// TODO
if (TREE_CODE(context) != RECORD_TYPE)
return NULL;
gcc_assert(TREE_CODE(context) == RECORD_TYPE);
type_name = TYPE_NAME(TYPE_MAIN_VARIANT(context));
if (type_name == NULL_TREE)
return NULL;
if (TREE_CODE(type_name) == IDENTIFIER_NODE)
return IDENTIFIER_POINTER(type_name);
else if (TREE_CODE(type_name) == TYPE_DECL)
return DECL_NAME_POINTER(type_name);
debug_tree(field_decl);
debug_tree(type_name);
gcc_unreachable();
}
// Was the function created by the compiler itself?
bool made_by_compiler(const_tree decl)
{
enum tree_code decl_code;
struct cgraph_node *node;
if (FUNCTION_PTR_P(decl))
return false;
decl_code = TREE_CODE(decl);
if (decl_code == VAR_DECL || decl_code == FIELD_DECL)
return false;
gcc_assert(decl_code == FUNCTION_DECL);
if (DECL_ABSTRACT_ORIGIN(decl) != NULL_TREE && DECL_ABSTRACT_ORIGIN(decl) != decl)
return true;
if (DECL_ARTIFICIAL(decl))
return true;
node = get_cnode(decl);
if (!node)
return false;
return node->clone_of != NULL;
}
bool skip_types(const_tree var)
{
const_tree type;
type = TREE_TYPE(var);
if (type == NULL_TREE)
return true;
switch (TREE_CODE(type)) {
case INTEGER_TYPE:
case ENUMERAL_TYPE:
return false;
default:
return true;
}
}
gimple get_fnptr_def_stmt(const_tree fn_ptr)
{
gimple def_stmt;
gcc_assert(fn_ptr != NULL_TREE);
gcc_assert(FUNCTION_PTR_P(fn_ptr));
if (is_gimple_constant(fn_ptr))
return NULL;
def_stmt = get_def_stmt(fn_ptr);
gcc_assert(def_stmt);
return def_stmt;
}
gimple get_def_stmt(const_tree node)
{
gcc_assert(node != NULL_TREE);
if (TREE_CODE(node) != SSA_NAME)
return NULL;
return SSA_NAME_DEF_STMT(node);
}
tree create_new_var(tree type)
{
tree new_var = create_tmp_var(type, "cicus");
add_referenced_var(new_var);
return new_var;
}
static bool skip_cast(tree dst_type, const_tree rhs, bool force)
{
const_gimple def_stmt = get_def_stmt(rhs);
if (force)
return false;
if (is_gimple_constant(rhs))
return false;
if (!def_stmt || gimple_code(def_stmt) == GIMPLE_NOP)
return false;
if (!types_compatible_p(dst_type, TREE_TYPE(rhs)))
return false;
// DI type can be on 32 bit (from create_assign) but overflow type stays DI
if (LONG_TYPE_SIZE == GET_MODE_BITSIZE(SImode))
return false;
return true;
}
tree cast_a_tree(tree type, tree var)
{
gcc_assert(type != NULL_TREE);
gcc_assert(var != NULL_TREE);
gcc_assert(fold_convertible_p(type, var));
return fold_convert(type, var);
}
gimple build_cast_stmt(struct visited *visited, tree dst_type, tree rhs, tree lhs, gimple_stmt_iterator *gsi, bool before, bool force)
{
gassign *assign;
gimple def_stmt;
gcc_assert(dst_type != NULL_TREE && rhs != NULL_TREE);
gcc_assert(!is_gimple_constant(rhs));
if (gsi_end_p(*gsi) && before == AFTER_STMT)
gcc_unreachable();
def_stmt = get_def_stmt(rhs);
if (def_stmt && gimple_code(def_stmt) != GIMPLE_NOP && skip_cast(dst_type, rhs, force) && pointer_set_contains(visited->my_stmts, def_stmt))
return def_stmt;
if (lhs == CREATE_NEW_VAR)
lhs = create_new_var(dst_type);
assign = gimple_build_assign(lhs, cast_a_tree(dst_type, rhs));
if (!gsi_end_p(*gsi)) {
location_t loc = gimple_location(gsi_stmt(*gsi));
gimple_set_location(assign, loc);
}
gimple_assign_set_lhs(assign, make_ssa_name(lhs, assign));
if (before)
gsi_insert_before(gsi, assign, GSI_NEW_STMT);
else
gsi_insert_after(gsi, assign, GSI_NEW_STMT);
update_stmt(assign);
return assign;
}
bool is_size_overflow_type(const_tree var)
{
const char *name;
const_tree type_name, type;
if (var == NULL_TREE)
return false;
type = TREE_TYPE(var);
type_name = TYPE_NAME(type);
if (type_name == NULL_TREE)
return false;
if (DECL_P(type_name))
name = DECL_NAME_POINTER(type_name);
else
name = IDENTIFIER_POINTER(type_name);
if (!strncmp(name, "size_overflow_type", 18))
return true;
return false;
}
// Determine if a cloned function has all the original arguments
static bool unchanged_arglist(struct cgraph_node *new_node, struct cgraph_node *old_node)
{
const_tree new_decl_list, old_decl_list;
if (new_node->clone_of && new_node->clone.tree_map)
return !new_node->clone.args_to_skip;
new_decl_list = DECL_ARGUMENTS(NODE_DECL(new_node));
old_decl_list = DECL_ARGUMENTS(NODE_DECL(old_node));
if (new_decl_list != NULL_TREE && old_decl_list != NULL_TREE)
gcc_assert(list_length(new_decl_list) == list_length(old_decl_list));
return true;
}
unsigned int get_correct_argnum_fndecl(const_tree fndecl, const_tree correct_argnum_of_fndecl, unsigned int num)
{
unsigned int new_num;
const_tree fndecl_arg;
tree fndecl_arglist = DECL_ARGUMENTS(fndecl);
const_tree arg, target_fndecl_arglist;
if (num == 0)
return num;
if (fndecl == correct_argnum_of_fndecl && !DECL_ARTIFICIAL(fndecl))
return num;
else if (fndecl == correct_argnum_of_fndecl && DECL_ARTIFICIAL(fndecl))
return CANNOT_FIND_ARG;
target_fndecl_arglist = DECL_ARGUMENTS(correct_argnum_of_fndecl);
if (fndecl_arglist == NULL_TREE || target_fndecl_arglist == NULL_TREE)
return CANNOT_FIND_ARG;
fndecl_arg = chain_index(num - 1, fndecl_arglist);
if (fndecl_arg == NULL_TREE)
return CANNOT_FIND_ARG;
for (arg = target_fndecl_arglist, new_num = 1; arg; arg = TREE_CHAIN(arg), new_num++) {
if (arg == fndecl_arg || !strcmp(DECL_NAME_POINTER(arg), DECL_NAME_POINTER(fndecl_arg)))
return new_num;
}
return CANNOT_FIND_ARG;
}
// Find the specified argument in the originally cloned function
static unsigned int clone_argnum_on_orig(struct cgraph_node *new_node, struct cgraph_node *old_node, unsigned int clone_argnum)
{
bitmap args_to_skip;
unsigned int i, new_argnum = clone_argnum;
if (unchanged_arglist(new_node, old_node))
return clone_argnum;
gcc_assert(new_node->clone_of && new_node->clone.tree_map);
args_to_skip = new_node->clone.args_to_skip;
for (i = 0; i < clone_argnum; i++) {
if (bitmap_bit_p(args_to_skip, i))
new_argnum++;
}
return new_argnum;
}
// Find the specified argument in the clone
static unsigned int orig_argnum_on_clone(struct cgraph_node *new_node, struct cgraph_node *old_node, unsigned int orig_argnum)
{
bitmap args_to_skip;
unsigned int i, new_argnum = orig_argnum;
if (unchanged_arglist(new_node, old_node))
return orig_argnum;
gcc_assert(new_node->clone_of && new_node->clone.tree_map);
args_to_skip = new_node->clone.args_to_skip;
if (bitmap_bit_p(args_to_skip, orig_argnum - 1))
// XXX torolni kellene a nodeot
return CANNOT_FIND_ARG;
for (i = 0; i < orig_argnum; i++) {
if (bitmap_bit_p(args_to_skip, i))
new_argnum--;
}
return new_argnum;
}
// Associate the argument between a clone and a cloned function
static unsigned int get_correct_argnum_cnode(struct cgraph_node *node, struct cgraph_node *correct_argnum_of_node, unsigned int argnum)
{
bool node_clone, correct_argnum_of_node_clone;
const_tree correct_argnum_of_node_decl, node_decl;
if (node == correct_argnum_of_node)
return argnum;
if (argnum == 0)
return argnum;
correct_argnum_of_node_decl = NODE_DECL(correct_argnum_of_node);
gcc_assert(correct_argnum_of_node_decl != NULL_TREE);
gcc_assert(correct_argnum_of_node && !DECL_ARTIFICIAL(correct_argnum_of_node_decl));
if (node) {
node_decl = NODE_DECL(node);
gcc_assert(!DECL_ARTIFICIAL(node_decl));
node_clone = made_by_compiler(node_decl);
} else {
node_decl = NULL_TREE;
node_clone = false;
}
if (correct_argnum_of_node_decl == node_decl)
return argnum;
correct_argnum_of_node_clone = made_by_compiler(correct_argnum_of_node_decl);
// the original decl is lost if both nodes are clones
if (node_clone && correct_argnum_of_node_clone) {
gcc_assert(unchanged_arglist(node, correct_argnum_of_node));
return argnum;
}
if (node_clone && !correct_argnum_of_node_clone)
return clone_argnum_on_orig(correct_argnum_of_node, node, argnum);
else if (!node_clone && correct_argnum_of_node_clone)
return orig_argnum_on_clone(correct_argnum_of_node, node, argnum);
if (node)
debug_tree(NODE_DECL(node));
debug_tree(correct_argnum_of_node_decl);
gcc_unreachable();
}
unsigned int get_correct_argnum(const_tree decl, const_tree correct_argnum_of_decl, unsigned int argnum)
{
struct cgraph_node *node, *correct_argnum_of_node;
gcc_assert(decl != NULL_TREE);
gcc_assert(correct_argnum_of_decl != NULL_TREE);
correct_argnum_of_node = get_cnode(correct_argnum_of_decl);
if (!correct_argnum_of_node || DECL_ARTIFICIAL(decl) || DECL_ARTIFICIAL(correct_argnum_of_decl))
return get_correct_argnum_fndecl(decl, correct_argnum_of_decl, argnum);
node = get_cnode(decl);
return get_correct_argnum_cnode(node, correct_argnum_of_node, argnum);
}
// Find the original cloned function
tree get_orig_fndecl(const_tree clone_fndecl)
{
struct cgraph_node *node;
gcc_assert(TREE_CODE(clone_fndecl) == FUNCTION_DECL);
if (DECL_ABSTRACT_ORIGIN(clone_fndecl))
return CONST_CAST_TREE(DECL_ABSTRACT_ORIGIN(clone_fndecl));
node = get_cnode(clone_fndecl);
if (!node)
return CONST_CAST_TREE(clone_fndecl);
while (node->clone_of)
node = node->clone_of;
if (!made_by_compiler(NODE_DECL(node)))
return NODE_DECL(node);
// Return the cloned decl because it is needed for the transform callback
return CONST_CAST_TREE(clone_fndecl);
}
static tree get_interesting_fndecl_from_stmt(const gcall *stmt)
{
if (gimple_call_num_args(stmt) == 0)
return NULL_TREE;
return gimple_call_fndecl(stmt);
}
tree get_interesting_orig_fndecl_from_stmt(const gcall *stmt)
{
tree fndecl;
fndecl = get_interesting_fndecl_from_stmt(stmt);
if (fndecl == NULL_TREE)
return NULL_TREE;
return get_orig_fndecl(fndecl);
}
void set_dominance_info(void)
{
calculate_dominance_info(CDI_DOMINATORS);
calculate_dominance_info(CDI_POST_DOMINATORS);
}
void unset_dominance_info(void)
{
free_dominance_info(CDI_DOMINATORS);
free_dominance_info(CDI_POST_DOMINATORS);
}
void set_current_function_decl(tree fndecl)
{
gcc_assert(fndecl != NULL_TREE);
push_cfun(DECL_STRUCT_FUNCTION(fndecl));
#if BUILDING_GCC_VERSION <= 4007
current_function_decl = fndecl;
#endif
set_dominance_info();
}
void unset_current_function_decl(void)
{
unset_dominance_info();
#if BUILDING_GCC_VERSION <= 4007
current_function_decl = NULL_TREE;
#endif
pop_cfun();
}
bool is_valid_cgraph_node(struct cgraph_node *node)
{
if (cgraph_function_body_availability(node) == AVAIL_NOT_AVAILABLE)
return false;
if (node->thunk.thunk_p || node->alias)
return false;
return true;
}
tree get_lhs(const_gimple stmt)
{
switch (gimple_code(stmt)) {
case GIMPLE_ASSIGN:
case GIMPLE_CALL:
return gimple_get_lhs(stmt);
case GIMPLE_PHI:
return gimple_phi_result(stmt);
default:
debug_gimple_stmt((gimple)stmt);
gcc_unreachable();
}
}