1334 lines
38 KiB
C
1334 lines
38 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"
|
|
#include "libiberty.h"
|
|
|
|
static void walk_use_def_next_functions(struct walk_use_def_data *use_def_data, tree lhs);
|
|
|
|
next_interesting_function_t global_next_interesting_function[GLOBAL_NIFN_LEN];
|
|
static bool global_changed;
|
|
|
|
static struct cgraph_node_hook_list *function_insertion_hook_holder;
|
|
static struct cgraph_2node_hook_list *node_duplication_hook_holder;
|
|
|
|
struct cgraph_node *get_cnode(const_tree fndecl)
|
|
{
|
|
gcc_assert(TREE_CODE(fndecl) == FUNCTION_DECL);
|
|
#if BUILDING_GCC_VERSION <= 4005
|
|
return cgraph_get_node(CONST_CAST_TREE(fndecl));
|
|
#else
|
|
return cgraph_get_node(fndecl);
|
|
#endif
|
|
}
|
|
|
|
static bool compare_next_interesting_functions(next_interesting_function_t cur_node, const char *decl_name, const char *context, unsigned int num)
|
|
{
|
|
// Ignore num without a value
|
|
if (num != NONE_ARGNUM && cur_node->num != num)
|
|
return false;
|
|
if (strcmp(cur_node->context, context))
|
|
return false;
|
|
return !strcmp(cur_node->decl_name, decl_name);
|
|
}
|
|
|
|
// Return the context of vardecl. If it is in a file scope then the context is vardecl_filebasename
|
|
static const char* get_vardecl_context(const_tree decl)
|
|
{
|
|
expanded_location xloc;
|
|
char *buf, *path;
|
|
const char *bname;
|
|
int len;
|
|
|
|
xloc = expand_location(DECL_SOURCE_LOCATION(decl));
|
|
gcc_assert(xloc.file);
|
|
path = xstrdup(xloc.file);
|
|
bname = lbasename(path);
|
|
|
|
len = asprintf(&buf, "vardecl_%s", bname);
|
|
gcc_assert(len > 0);
|
|
return buf;
|
|
}
|
|
|
|
// Return the type name for a function pointer (or "fielddecl" if the type has no name), otherwise either "vardecl" or "fndecl"
|
|
const char* get_decl_context(const_tree decl)
|
|
{
|
|
switch (TREE_CODE(decl)) {
|
|
case FUNCTION_DECL:
|
|
return "fndecl";
|
|
// TODO: Ignore anonymous types for now
|
|
case FIELD_DECL:
|
|
return get_type_name_from_field(decl);
|
|
case VAR_DECL:
|
|
if (TREE_PUBLIC(decl) || DECL_EXTERNAL(decl))
|
|
return "vardecl";
|
|
if (TREE_STATIC(decl) && !TREE_PUBLIC(decl))
|
|
return get_vardecl_context(decl);
|
|
// ignore local variable
|
|
if (!TREE_STATIC(decl) && !DECL_EXTERNAL(decl))
|
|
return NULL;
|
|
default:
|
|
debug_tree(decl);
|
|
gcc_unreachable();
|
|
}
|
|
}
|
|
|
|
// Find the function with the specified argument in the list
|
|
next_interesting_function_t get_global_next_interesting_function_entry(struct fn_raw_data *raw_data)
|
|
{
|
|
next_interesting_function_t cur_node, head;
|
|
|
|
gcc_assert(raw_data->hash != NO_HASH);
|
|
gcc_assert(raw_data->decl_str);
|
|
gcc_assert(raw_data->context);
|
|
|
|
head = global_next_interesting_function[raw_data->hash];
|
|
for (cur_node = head; cur_node; cur_node = cur_node->next) {
|
|
if (raw_data->marked != ASM_STMT_SO_MARK && cur_node->marked == ASM_STMT_SO_MARK)
|
|
continue;
|
|
if (compare_next_interesting_functions(cur_node, raw_data->decl_str, raw_data->context, raw_data->num))
|
|
return cur_node;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
next_interesting_function_t get_global_next_interesting_function_entry_with_hash(struct fn_raw_data *raw_data)
|
|
{
|
|
gcc_assert(raw_data->decl != NULL_TREE);
|
|
gcc_assert(raw_data->decl_str);
|
|
|
|
raw_data->hash = get_decl_hash(raw_data->decl, raw_data->decl_str);
|
|
if (raw_data->hash == NO_HASH)
|
|
return NULL;
|
|
|
|
if (!raw_data->context)
|
|
raw_data->context = get_decl_context(raw_data->decl);
|
|
if (!raw_data->context)
|
|
return NULL;
|
|
return get_global_next_interesting_function_entry(raw_data);
|
|
}
|
|
|
|
next_interesting_function_t create_new_next_interesting_entry(struct fn_raw_data *raw_data, next_interesting_function_t orig_next_node)
|
|
{
|
|
next_interesting_function_t new_node;
|
|
|
|
gcc_assert(raw_data->decl_str);
|
|
gcc_assert(raw_data->context);
|
|
gcc_assert(raw_data->hash != NO_HASH);
|
|
gcc_assert(raw_data->num != NONE_ARGNUM);
|
|
gcc_assert(raw_data->based_decl != SO_NONE);
|
|
|
|
new_node = (next_interesting_function_t)xmalloc(sizeof(*new_node));
|
|
new_node->decl_name = xstrdup(raw_data->decl_str);
|
|
|
|
gcc_assert(raw_data->context);
|
|
new_node->context = xstrdup(raw_data->context);
|
|
new_node->hash = raw_data->hash;
|
|
new_node->num = raw_data->num;
|
|
new_node->next = NULL;
|
|
new_node->children = NULL;
|
|
new_node->marked = raw_data->marked;
|
|
new_node->orig_next_node = orig_next_node;
|
|
new_node->based_decl = raw_data->based_decl;
|
|
|
|
return new_node;
|
|
}
|
|
|
|
// Ignore these functions to not explode coverage (+strncmp+fndecl+3+35130+)
|
|
static bool temporary_skip_these_functions(struct fn_raw_data *raw_data)
|
|
{
|
|
gcc_assert(raw_data->hash != NO_HASH);
|
|
gcc_assert(raw_data->decl_str);
|
|
|
|
if (raw_data->hash == 35130 && !strcmp(raw_data->decl_str, "strncmp"))
|
|
return true;
|
|
if (raw_data->hash == 46193 && !strcmp(raw_data->decl_str, "strnlen"))
|
|
return true;
|
|
if (raw_data->hash == 43267 && !strcmp(raw_data->decl_str, "strncpy"))
|
|
return true;
|
|
if (raw_data->hash == 10300 && !strcmp(raw_data->decl_str, "strncpy_from_user"))
|
|
return true;
|
|
if (raw_data->hash == 26117 && !strcmp(raw_data->decl_str, "memchr"))
|
|
return true;
|
|
if (raw_data->hash == 16203 && !strcmp(raw_data->decl_str, "memchr_inv"))
|
|
return true;
|
|
if (raw_data->hash == 24269 && !strcmp(raw_data->decl_str, "memcmp"))
|
|
return true;
|
|
if (raw_data->hash == 60390 && !strcmp(raw_data->decl_str, "memcpy"))
|
|
return true;
|
|
if (raw_data->hash == 25040 && !strcmp(raw_data->decl_str, "memmove"))
|
|
return true;
|
|
if (raw_data->hash == 29763 && !strcmp(raw_data->decl_str, "memset"))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// Create the main data structure
|
|
next_interesting_function_t create_new_next_interesting_decl(struct fn_raw_data *raw_data, next_interesting_function_t orig_next_node)
|
|
{
|
|
enum tree_code decl_code;
|
|
|
|
if (raw_data->num == NONE_ARGNUM)
|
|
return NULL;
|
|
|
|
gcc_assert(raw_data->decl != NULL_TREE);
|
|
gcc_assert(raw_data->num != NONE_ARGNUM);
|
|
gcc_assert(raw_data->decl_str);
|
|
|
|
decl_code = TREE_CODE(raw_data->decl);
|
|
|
|
gcc_assert(decl_code == FIELD_DECL || decl_code == FUNCTION_DECL || decl_code == VAR_DECL);
|
|
|
|
if (is_vararg(raw_data->decl, raw_data->num))
|
|
return NULL;
|
|
|
|
raw_data->hash = get_decl_hash(raw_data->decl, raw_data->decl_str);
|
|
if (raw_data->hash == NO_HASH)
|
|
return NULL;
|
|
|
|
if (get_size_overflow_hash_entry_tree(raw_data, DISABLE_SIZE_OVERFLOW))
|
|
return NULL;
|
|
if (temporary_skip_these_functions(raw_data))
|
|
return NULL;
|
|
|
|
gcc_assert(raw_data->num <= MAX_PARAM);
|
|
// Clones must have an orig_next_node
|
|
gcc_assert(!made_by_compiler(raw_data->decl) || orig_next_node);
|
|
|
|
raw_data->context = get_decl_context(raw_data->decl);
|
|
if (!raw_data->context)
|
|
return NULL;
|
|
|
|
return create_new_next_interesting_entry(raw_data, orig_next_node);
|
|
}
|
|
|
|
void add_to_global_next_interesting_function(next_interesting_function_t new_entry)
|
|
{
|
|
next_interesting_function_t cur_global_head, cur_global, cur_global_end = NULL;
|
|
|
|
// new_entry is appended to the end of a list
|
|
new_entry->next = NULL;
|
|
|
|
cur_global_head = global_next_interesting_function[new_entry->hash];
|
|
if (!cur_global_head) {
|
|
global_next_interesting_function[new_entry->hash] = new_entry;
|
|
return;
|
|
}
|
|
|
|
|
|
for (cur_global = cur_global_head; cur_global; cur_global = cur_global->next) {
|
|
if (!cur_global->next)
|
|
cur_global_end = cur_global;
|
|
|
|
if (compare_next_interesting_functions(cur_global, new_entry->decl_name, new_entry->context, new_entry->num))
|
|
return;
|
|
}
|
|
|
|
gcc_assert(cur_global_end);
|
|
cur_global_end->next = new_entry;
|
|
}
|
|
|
|
/* If the interesting function is a clone then find or create its original next_interesting_function_t node
|
|
* and add it to global_next_interesting_function
|
|
*/
|
|
static next_interesting_function_t create_orig_next_node_for_a_clone(struct fn_raw_data *clone_raw_data)
|
|
{
|
|
struct fn_raw_data orig_raw_data;
|
|
next_interesting_function_t orig_next_node;
|
|
enum tree_code decl_code;
|
|
|
|
gcc_assert(clone_raw_data->decl != NULL_TREE);
|
|
gcc_assert(clone_raw_data->num != NONE_ARGNUM);
|
|
gcc_assert(clone_raw_data->based_decl != SO_NONE);
|
|
|
|
initialize_raw_data(&orig_raw_data);
|
|
orig_raw_data.decl = get_orig_fndecl(clone_raw_data->decl);
|
|
|
|
if (DECL_BUILT_IN(orig_raw_data.decl) || DECL_BUILT_IN_CLASS(orig_raw_data.decl) == BUILT_IN_NORMAL)
|
|
return NULL;
|
|
|
|
if (made_by_compiler(orig_raw_data.decl))
|
|
return NULL;
|
|
|
|
decl_code = TREE_CODE(orig_raw_data.decl);
|
|
if (decl_code == FIELD_DECL || decl_code == VAR_DECL)
|
|
orig_raw_data.num = clone_raw_data->num;
|
|
else
|
|
orig_raw_data.num = get_correct_argnum(clone_raw_data->decl, orig_raw_data.decl, clone_raw_data->num);
|
|
|
|
// Skip over ISRA.162 parm decls
|
|
if (orig_raw_data.num == CANNOT_FIND_ARG)
|
|
return NULL;
|
|
|
|
orig_raw_data.decl_str = get_orig_decl_name(orig_raw_data.decl);
|
|
orig_raw_data.marked = NO_SO_MARK;
|
|
orig_next_node = get_global_next_interesting_function_entry_with_hash(&orig_raw_data);
|
|
if (orig_next_node)
|
|
return orig_next_node;
|
|
|
|
orig_raw_data.marked = clone_raw_data->marked;
|
|
orig_raw_data.based_decl = clone_raw_data->based_decl;
|
|
orig_next_node = create_new_next_interesting_decl(&orig_raw_data, NULL);
|
|
if (!orig_next_node)
|
|
return NULL;
|
|
|
|
add_to_global_next_interesting_function(orig_next_node);
|
|
return orig_next_node;
|
|
}
|
|
|
|
// Find or create the next_interesting_function_t node for decl and num
|
|
next_interesting_function_t get_and_create_next_node_from_global_next_nodes(struct fn_raw_data *raw_data, next_interesting_function_t orig_next_node)
|
|
{
|
|
next_interesting_function_t cur_next_cnode;
|
|
|
|
gcc_assert(raw_data->decl != NULL_TREE);
|
|
|
|
if (DECL_NAME(raw_data->decl) == NULL_TREE)
|
|
return NULL;
|
|
|
|
raw_data->decl_str = DECL_NAME_POINTER(raw_data->decl);
|
|
|
|
cur_next_cnode = get_global_next_interesting_function_entry_with_hash(raw_data);
|
|
if (cur_next_cnode)
|
|
goto out;
|
|
|
|
if (!orig_next_node && made_by_compiler(raw_data->decl)) {
|
|
orig_next_node = create_orig_next_node_for_a_clone(raw_data);
|
|
if (!orig_next_node)
|
|
return NULL;
|
|
}
|
|
|
|
cur_next_cnode = create_new_next_interesting_decl(raw_data, orig_next_node);
|
|
if (!cur_next_cnode)
|
|
return NULL;
|
|
|
|
add_to_global_next_interesting_function(cur_next_cnode);
|
|
out:
|
|
if (cur_next_cnode->marked != raw_data->marked && cur_next_cnode->marked != NO_SO_MARK)
|
|
return cur_next_cnode;
|
|
|
|
if (raw_data->marked != NO_SO_MARK && cur_next_cnode->marked == NO_SO_MARK)
|
|
cur_next_cnode->marked = raw_data->marked;
|
|
|
|
return cur_next_cnode;
|
|
}
|
|
|
|
static bool has_next_interesting_function_chain_node(next_interesting_function_t next_cnodes_head, struct fn_raw_data *raw_data)
|
|
{
|
|
next_interesting_function_t cur_node;
|
|
|
|
gcc_assert(!raw_data->context);
|
|
|
|
gcc_assert(raw_data->decl_str);
|
|
gcc_assert(raw_data->decl != NULL_TREE);
|
|
gcc_assert(raw_data->num != NONE_ARGNUM);
|
|
|
|
raw_data->context = get_decl_context(raw_data->decl);
|
|
// Ignore function if there is no context
|
|
if (!raw_data->context)
|
|
return true;
|
|
|
|
for (cur_node = next_cnodes_head; cur_node; cur_node = cur_node->next) {
|
|
if (compare_next_interesting_functions(cur_node, raw_data->decl_str, raw_data->context, raw_data->num))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void handle_function(struct walk_use_def_data *use_def_data, tree fndecl, const_tree arg)
|
|
{
|
|
struct fn_raw_data raw_data;
|
|
next_interesting_function_t orig_next_node, new_node;
|
|
|
|
gcc_assert(fndecl != NULL_TREE);
|
|
|
|
// ignore builtins to not explode coverage (e.g., memcpy)
|
|
if (DECL_BUILT_IN(fndecl) || DECL_BUILT_IN_CLASS(fndecl) == BUILT_IN_NORMAL)
|
|
return;
|
|
|
|
if (get_intentional_attr_type(fndecl) == MARK_TURN_OFF)
|
|
return;
|
|
|
|
initialize_raw_data(&raw_data);
|
|
raw_data.decl = fndecl;
|
|
raw_data.based_decl = SO_FUNCTION;
|
|
raw_data.decl_str = DECL_NAME_POINTER(fndecl);
|
|
raw_data.marked = NO_SO_MARK;
|
|
|
|
// convert arg into its position
|
|
if (arg == NULL_TREE)
|
|
raw_data.num = 0;
|
|
else
|
|
raw_data.num = find_arg_number_tree(arg, raw_data.decl);
|
|
if (raw_data.num == CANNOT_FIND_ARG)
|
|
return;
|
|
|
|
if (has_next_interesting_function_chain_node(use_def_data->next_cnodes_head, &raw_data))
|
|
return;
|
|
|
|
if (made_by_compiler(raw_data.decl)) {
|
|
orig_next_node = create_orig_next_node_for_a_clone(&raw_data);
|
|
if (!orig_next_node)
|
|
return;
|
|
} else
|
|
orig_next_node = NULL;
|
|
|
|
new_node = create_new_next_interesting_decl(&raw_data, orig_next_node);
|
|
if (!new_node)
|
|
return;
|
|
new_node->next = use_def_data->next_cnodes_head;
|
|
use_def_data->next_cnodes_head = new_node;
|
|
}
|
|
|
|
static void walk_use_def_next_functions_phi(struct walk_use_def_data *use_def_data, const_tree result)
|
|
{
|
|
gphi *phi = as_a_gphi(get_def_stmt(result));
|
|
unsigned int i, n = gimple_phi_num_args(phi);
|
|
|
|
pointer_set_insert(use_def_data->visited, phi);
|
|
for (i = 0; i < n; i++) {
|
|
tree arg = gimple_phi_arg_def(phi, i);
|
|
|
|
walk_use_def_next_functions(use_def_data, arg);
|
|
}
|
|
}
|
|
|
|
static void walk_use_def_next_functions_binary(struct walk_use_def_data *use_def_data, const_tree lhs)
|
|
{
|
|
gassign *def_stmt = as_a_gassign(get_def_stmt(lhs));
|
|
tree rhs1, rhs2;
|
|
|
|
rhs1 = gimple_assign_rhs1(def_stmt);
|
|
rhs2 = gimple_assign_rhs2(def_stmt);
|
|
|
|
walk_use_def_next_functions(use_def_data, rhs1);
|
|
walk_use_def_next_functions(use_def_data, rhs2);
|
|
}
|
|
|
|
static void walk_use_def_next_functions_unary(struct walk_use_def_data *use_def_data, const gassign *stmt)
|
|
{
|
|
tree rhs1 = gimple_assign_rhs1(stmt);
|
|
|
|
walk_use_def_next_functions(use_def_data, rhs1);
|
|
}
|
|
|
|
void __weak handle_function_ptr_ret(struct walk_use_def_data *use_def_data __unused, const_tree fn_ptr __unused)
|
|
{
|
|
}
|
|
|
|
static void create_and_append_new_next_interesting_field_var_decl(struct walk_use_def_data *use_def_data, struct fn_raw_data *raw_data)
|
|
{
|
|
next_interesting_function_t new_node;
|
|
|
|
if (raw_data->decl == NULL_TREE)
|
|
return;
|
|
|
|
if (DECL_NAME(raw_data->decl) == NULL_TREE)
|
|
return;
|
|
|
|
gcc_assert(!raw_data->decl_str);
|
|
gcc_assert(raw_data->num == NONE_ARGNUM);
|
|
|
|
raw_data->decl_str = DECL_NAME_POINTER(raw_data->decl);
|
|
raw_data->num = 0;
|
|
raw_data->marked = NO_SO_MARK;
|
|
|
|
new_node = create_new_next_interesting_decl(raw_data, NULL);
|
|
if (!new_node)
|
|
return;
|
|
new_node->next = use_def_data->next_cnodes_head;
|
|
use_def_data->next_cnodes_head = new_node;
|
|
}
|
|
|
|
static void handle_struct_fields(struct walk_use_def_data *use_def_data, const_tree node)
|
|
{
|
|
struct fn_raw_data raw_data;
|
|
|
|
initialize_raw_data(&raw_data);
|
|
|
|
switch (TREE_CODE(node)) {
|
|
case ARRAY_REF:
|
|
#if BUILDING_GCC_VERSION >= 4006
|
|
case MEM_REF:
|
|
#endif
|
|
case INDIRECT_REF:
|
|
case COMPONENT_REF:
|
|
raw_data.decl = get_ref_field(node);
|
|
raw_data.based_decl = SO_FIELD;
|
|
break;
|
|
// TODO
|
|
case BIT_FIELD_REF:
|
|
case VIEW_CONVERT_EXPR:
|
|
case REALPART_EXPR:
|
|
case IMAGPART_EXPR:
|
|
return;
|
|
default:
|
|
debug_tree(node);
|
|
gcc_unreachable();
|
|
}
|
|
|
|
if (get_intentional_attr_type(raw_data.decl) == MARK_TURN_OFF)
|
|
return;
|
|
|
|
create_and_append_new_next_interesting_field_var_decl(use_def_data, &raw_data);
|
|
}
|
|
|
|
static void handle_vardecl(struct walk_use_def_data *use_def_data, tree node)
|
|
{
|
|
struct fn_raw_data raw_data;
|
|
|
|
initialize_raw_data(&raw_data);
|
|
|
|
raw_data.decl = node;
|
|
raw_data.based_decl = SO_VAR;
|
|
create_and_append_new_next_interesting_field_var_decl(use_def_data, &raw_data);
|
|
}
|
|
|
|
/* Find all functions that influence lhs
|
|
*
|
|
* Encountered functions are added to the children vector (next_interesting_function_t).
|
|
*/
|
|
static void walk_use_def_next_functions(struct walk_use_def_data *use_def_data, tree lhs)
|
|
{
|
|
enum tree_code code;
|
|
const_gimple def_stmt;
|
|
|
|
if (skip_types(lhs))
|
|
return;
|
|
|
|
if (VAR_P(lhs)) {
|
|
handle_vardecl(use_def_data, lhs);
|
|
return;
|
|
}
|
|
|
|
code = TREE_CODE(lhs);
|
|
if (code == PARM_DECL) {
|
|
handle_function(use_def_data, current_function_decl, lhs);
|
|
return;
|
|
}
|
|
|
|
if (TREE_CODE_CLASS(code) == tcc_reference) {
|
|
handle_struct_fields(use_def_data, lhs);
|
|
return;
|
|
}
|
|
|
|
if (code != SSA_NAME)
|
|
return;
|
|
|
|
def_stmt = get_def_stmt(lhs);
|
|
if (!def_stmt)
|
|
return;
|
|
|
|
if (pointer_set_insert(use_def_data->visited, def_stmt))
|
|
return;
|
|
|
|
switch (gimple_code(def_stmt)) {
|
|
case GIMPLE_NOP:
|
|
walk_use_def_next_functions(use_def_data, SSA_NAME_VAR(lhs));
|
|
return;
|
|
case GIMPLE_ASM:
|
|
if (!is_size_overflow_asm(def_stmt))
|
|
return;
|
|
walk_use_def_next_functions(use_def_data, get_size_overflow_asm_input(as_a_const_gasm(def_stmt)));
|
|
return;
|
|
case GIMPLE_CALL: {
|
|
tree fndecl = gimple_call_fndecl(def_stmt);
|
|
|
|
if (fndecl != NULL_TREE) {
|
|
handle_function(use_def_data, fndecl, NULL_TREE);
|
|
return;
|
|
}
|
|
fndecl = gimple_call_fn(def_stmt);
|
|
handle_function_ptr_ret(use_def_data, fndecl);
|
|
return;
|
|
}
|
|
case GIMPLE_PHI:
|
|
walk_use_def_next_functions_phi(use_def_data, lhs);
|
|
return;
|
|
case GIMPLE_ASSIGN:
|
|
switch (gimple_num_ops(def_stmt)) {
|
|
case 2:
|
|
walk_use_def_next_functions_unary(use_def_data, as_a_const_gassign(def_stmt));
|
|
return;
|
|
case 3:
|
|
walk_use_def_next_functions_binary(use_def_data, lhs);
|
|
return;
|
|
}
|
|
default:
|
|
debug_gimple_stmt((gimple)def_stmt);
|
|
error("%s: unknown gimple code", __func__);
|
|
gcc_unreachable();
|
|
}
|
|
}
|
|
|
|
// Start the search for next_interesting_function_t children based on the (next_interesting_function_t) parent node
|
|
static next_interesting_function_t search_next_functions(tree node, next_interesting_function_t parent)
|
|
{
|
|
struct walk_use_def_data use_def_data;
|
|
|
|
use_def_data.parent = parent;
|
|
use_def_data.next_cnodes_head = NULL;
|
|
use_def_data.visited = pointer_set_create();
|
|
|
|
walk_use_def_next_functions(&use_def_data, node);
|
|
|
|
pointer_set_destroy(use_def_data.visited);
|
|
return use_def_data.next_cnodes_head;
|
|
}
|
|
|
|
// True if child already exists in the next_interesting_function_t children vector
|
|
bool has_next_interesting_function_vec(next_interesting_function_t target, next_interesting_function_t next_node)
|
|
{
|
|
unsigned int i;
|
|
next_interesting_function_t cur;
|
|
|
|
gcc_assert(next_node);
|
|
// handle recursion
|
|
if (!strcmp(target->decl_name, next_node->decl_name) && target->num == next_node->num)
|
|
return true;
|
|
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
if (VEC_empty(next_interesting_function_t, target->children))
|
|
return false;
|
|
FOR_EACH_VEC_ELT(next_interesting_function_t, target->children, i, cur) {
|
|
#else
|
|
FOR_EACH_VEC_SAFE_ELT(target->children, i, cur) {
|
|
#endif
|
|
if (compare_next_interesting_functions(cur, next_node->decl_name, next_node->context, next_node->num))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void push_child(next_interesting_function_t parent, next_interesting_function_t child)
|
|
{
|
|
if (!has_next_interesting_function_vec(parent, child)) {
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
VEC_safe_push(next_interesting_function_t, heap, parent->children, child);
|
|
#else
|
|
vec_safe_push(parent->children, child);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void __weak check_local_variables(next_interesting_function_t next_node __unused) {}
|
|
|
|
// Add children to parent and global_next_interesting_function
|
|
static void collect_data_for_execute(next_interesting_function_t parent, next_interesting_function_t children)
|
|
{
|
|
next_interesting_function_t cur = children;
|
|
|
|
gcc_assert(parent);
|
|
|
|
while (cur) {
|
|
struct fn_raw_data child_raw_data;
|
|
next_interesting_function_t next, child;
|
|
|
|
next = cur->next;
|
|
|
|
initialize_raw_data(&child_raw_data);
|
|
child_raw_data.decl_str = cur->decl_name;
|
|
child_raw_data.context = cur->context;
|
|
child_raw_data.hash = cur->hash;
|
|
child_raw_data.num = cur->num;
|
|
child_raw_data.marked = NO_SO_MARK;
|
|
child = get_global_next_interesting_function_entry(&child_raw_data);
|
|
if (!child) {
|
|
add_to_global_next_interesting_function(cur);
|
|
child = cur;
|
|
}
|
|
|
|
check_local_variables(child);
|
|
|
|
push_child(parent, child);
|
|
|
|
cur = next;
|
|
}
|
|
|
|
check_local_variables(parent);
|
|
}
|
|
|
|
next_interesting_function_t __weak get_and_create_next_node_from_global_next_nodes_fnptr(const_tree fn_ptr __unused, struct fn_raw_data *raw_data __unused)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static next_interesting_function_t create_parent_next_cnode(const_gimple stmt, unsigned int num)
|
|
{
|
|
struct fn_raw_data raw_data;
|
|
|
|
initialize_raw_data(&raw_data);
|
|
raw_data.num = num;
|
|
raw_data.marked = NO_SO_MARK;
|
|
raw_data.based_decl = SO_FUNCTION;
|
|
|
|
switch (gimple_code(stmt)) {
|
|
case GIMPLE_ASM:
|
|
raw_data.decl = current_function_decl;
|
|
raw_data.marked = ASM_STMT_SO_MARK;
|
|
return get_and_create_next_node_from_global_next_nodes(&raw_data, NULL);
|
|
case GIMPLE_CALL:
|
|
raw_data.decl = gimple_call_fndecl(stmt);
|
|
if (raw_data.decl != NULL_TREE)
|
|
return get_and_create_next_node_from_global_next_nodes(&raw_data, NULL);
|
|
raw_data.decl = gimple_call_fn(stmt);
|
|
return get_and_create_next_node_from_global_next_nodes_fnptr(raw_data.decl, &raw_data);
|
|
case GIMPLE_RETURN:
|
|
raw_data.decl = current_function_decl;
|
|
return get_and_create_next_node_from_global_next_nodes(&raw_data, NULL);
|
|
case GIMPLE_ASSIGN: {
|
|
tree lhs = gimple_assign_lhs(stmt);
|
|
|
|
if (VAR_P(lhs))
|
|
raw_data.decl = lhs;
|
|
else
|
|
raw_data.decl = get_ref_field(lhs);
|
|
if (raw_data.decl == NULL_TREE)
|
|
return NULL;
|
|
return get_and_create_next_node_from_global_next_nodes(&raw_data, NULL);
|
|
}
|
|
default:
|
|
debug_gimple_stmt((gimple)stmt);
|
|
gcc_unreachable();
|
|
}
|
|
}
|
|
|
|
// Handle potential next_interesting_function_t parent if its argument has an integer type
|
|
static void collect_all_possible_size_overflow_fns(const_gimple stmt, tree start_var, unsigned int num)
|
|
{
|
|
next_interesting_function_t children_next_cnode, parent_next_cnode;
|
|
|
|
// skip void return values
|
|
if (start_var == NULL_TREE)
|
|
return;
|
|
|
|
if (skip_types(start_var))
|
|
return;
|
|
|
|
// handle intentional MARK_TURN_OFF
|
|
if (check_intentional_size_overflow_asm_and_attribute(start_var) == MARK_TURN_OFF)
|
|
return;
|
|
|
|
parent_next_cnode = create_parent_next_cnode(stmt, num);
|
|
if (!parent_next_cnode)
|
|
return;
|
|
|
|
children_next_cnode = search_next_functions(start_var, parent_next_cnode);
|
|
collect_data_for_execute(parent_next_cnode, children_next_cnode);
|
|
}
|
|
|
|
static void collect_all_possible_size_overflow_fields_and_vars(const gassign *assign)
|
|
{
|
|
tree start_var, decl, lhs = gimple_assign_lhs(assign);
|
|
|
|
if (VAR_P(lhs))
|
|
decl = lhs;
|
|
else
|
|
decl = get_ref_field(lhs);
|
|
if (decl == NULL_TREE)
|
|
return;
|
|
|
|
if (get_intentional_attr_type(decl) == MARK_TURN_OFF)
|
|
return;
|
|
|
|
start_var = gimple_assign_rhs1(assign);
|
|
collect_all_possible_size_overflow_fns(assign, start_var, 0);
|
|
|
|
start_var = gimple_assign_rhs2(assign);
|
|
collect_all_possible_size_overflow_fns(assign, start_var, 0);
|
|
|
|
#if BUILDING_GCC_VERSION >= 4006
|
|
start_var = gimple_assign_rhs3(assign);
|
|
collect_all_possible_size_overflow_fns(assign, start_var, 0);
|
|
#endif
|
|
}
|
|
|
|
// Find potential next_interesting_function_t parents
|
|
static void handle_cgraph_node(struct cgraph_node *node)
|
|
{
|
|
basic_block bb;
|
|
tree cur_fndecl = NODE_DECL(node);
|
|
|
|
set_current_function_decl(cur_fndecl);
|
|
|
|
FOR_EACH_BB_FN(bb, cfun) {
|
|
gimple_stmt_iterator gsi;
|
|
|
|
for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
|
|
tree start_var;
|
|
gimple stmt = gsi_stmt(gsi);
|
|
|
|
switch (gimple_code(stmt)) {
|
|
case GIMPLE_RETURN: {
|
|
const greturn *return_stmt = as_a_const_greturn(stmt);
|
|
|
|
start_var = gimple_return_retval(return_stmt);
|
|
collect_all_possible_size_overflow_fns(return_stmt, start_var, 0);
|
|
break;
|
|
}
|
|
case GIMPLE_ASM: {
|
|
const gasm *asm_stmt = as_a_const_gasm(stmt);
|
|
|
|
if (!is_size_overflow_insert_check_asm(asm_stmt))
|
|
break;
|
|
start_var = get_size_overflow_asm_input(asm_stmt);
|
|
collect_all_possible_size_overflow_fns(asm_stmt, start_var, 0);
|
|
break;
|
|
}
|
|
case GIMPLE_CALL: {
|
|
unsigned int i, len;
|
|
const gcall *call = as_a_const_gcall(stmt);
|
|
tree fndecl = gimple_call_fndecl(call);
|
|
|
|
if (fndecl != NULL_TREE && (DECL_BUILT_IN(fndecl) || DECL_BUILT_IN_CLASS(fndecl) == BUILT_IN_NORMAL))
|
|
break;
|
|
|
|
len = gimple_call_num_args(call);
|
|
for (i = 0; i < len; i++) {
|
|
start_var = gimple_call_arg(call, i);
|
|
collect_all_possible_size_overflow_fns(call, start_var, i + 1);
|
|
}
|
|
break;
|
|
}
|
|
case GIMPLE_ASSIGN:
|
|
collect_all_possible_size_overflow_fields_and_vars(as_a_const_gassign(stmt));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
unset_current_function_decl();
|
|
}
|
|
|
|
/* Collect all potentially interesting function parameters and return values of integer types
|
|
* and store their data flow dependencies
|
|
*/
|
|
static void size_overflow_generate_summary(void)
|
|
{
|
|
struct cgraph_node *node;
|
|
|
|
size_overflow_register_hooks();
|
|
|
|
FOR_EACH_FUNCTION(node) {
|
|
if (is_valid_cgraph_node(node))
|
|
handle_cgraph_node(node);
|
|
}
|
|
}
|
|
|
|
static void size_overflow_function_insertion_hook(struct cgraph_node *node __unused, void *data __unused)
|
|
{
|
|
debug_cgraph_node(node);
|
|
gcc_unreachable();
|
|
}
|
|
|
|
/* Handle dst if src is in the global_next_interesting_function list.
|
|
* If src is a clone then dst inherits the orig_next_node of src otherwise
|
|
* src will become the orig_next_node of dst.
|
|
*/
|
|
static void size_overflow_node_duplication_hook(struct cgraph_node *src, struct cgraph_node *dst, void *data __unused)
|
|
{
|
|
next_interesting_function_t head, cur;
|
|
struct fn_raw_data src_raw_data;
|
|
|
|
initialize_raw_data(&src_raw_data);
|
|
src_raw_data.decl = NODE_DECL(src);
|
|
src_raw_data.decl_str = DECL_NAME_POINTER(src_raw_data.decl);
|
|
src_raw_data.context = get_decl_context(src_raw_data.decl);
|
|
if (!src_raw_data.context)
|
|
return;
|
|
|
|
src_raw_data.num = NONE_ARGNUM;
|
|
src_raw_data.marked = NO_SO_MARK;
|
|
|
|
head = get_global_next_interesting_function_entry_with_hash(&src_raw_data);
|
|
if (!head)
|
|
return;
|
|
|
|
for (cur = head; cur; cur = cur->next) {
|
|
struct fn_raw_data dst_raw_data;
|
|
next_interesting_function_t orig_next_node, next_node;
|
|
|
|
if (!compare_next_interesting_functions(cur, src_raw_data.decl_str, src_raw_data.context, src_raw_data.num))
|
|
continue;
|
|
|
|
initialize_raw_data(&dst_raw_data);
|
|
dst_raw_data.decl = NODE_DECL(dst);
|
|
dst_raw_data.decl_str = cgraph_node_name(dst);
|
|
dst_raw_data.marked = cur->marked;
|
|
dst_raw_data.based_decl = cur->based_decl;
|
|
|
|
if (!made_by_compiler(dst_raw_data.decl))
|
|
break;
|
|
|
|
// For clones use the original node instead
|
|
if (cur->orig_next_node)
|
|
orig_next_node = cur->orig_next_node;
|
|
else
|
|
orig_next_node = cur;
|
|
|
|
dst_raw_data.num = get_correct_argnum_fndecl(src_raw_data.decl, dst_raw_data.decl, cur->num);
|
|
if (dst_raw_data.num == CANNOT_FIND_ARG)
|
|
continue;
|
|
|
|
next_node = create_new_next_interesting_decl(&dst_raw_data, orig_next_node);
|
|
if (next_node)
|
|
add_to_global_next_interesting_function(next_node);
|
|
}
|
|
}
|
|
|
|
void size_overflow_register_hooks(void)
|
|
{
|
|
static bool init_p = false;
|
|
|
|
if (init_p)
|
|
return;
|
|
init_p = true;
|
|
|
|
function_insertion_hook_holder = cgraph_add_function_insertion_hook(&size_overflow_function_insertion_hook, NULL);
|
|
node_duplication_hook_holder = cgraph_add_node_duplication_hook(&size_overflow_node_duplication_hook, NULL);
|
|
}
|
|
|
|
static void set_yes_so_mark(next_interesting_function_t next_node)
|
|
{
|
|
if (next_node->marked == NO_SO_MARK) {
|
|
next_node->marked = YES_SO_MARK;
|
|
global_changed = true;
|
|
}
|
|
// Mark the orig decl as well if it's a clone
|
|
if (next_node->orig_next_node && next_node->orig_next_node->marked == NO_SO_MARK) {
|
|
next_node->orig_next_node->marked = YES_SO_MARK;
|
|
global_changed = true;
|
|
}
|
|
}
|
|
|
|
// Determine whether node or orig node is part of a tracked data flow
|
|
static bool marked_fn(next_interesting_function_t next_node)
|
|
{
|
|
bool is_marked_fn, is_marked_orig = false;
|
|
|
|
is_marked_fn = next_node->marked != NO_SO_MARK;
|
|
|
|
if (next_node->orig_next_node)
|
|
is_marked_orig = next_node->orig_next_node->marked != NO_SO_MARK;
|
|
|
|
return is_marked_fn || is_marked_orig;
|
|
}
|
|
|
|
// Determine whether node or orig node is in the hash table already
|
|
static bool already_in_the_hashtable(next_interesting_function_t next_node)
|
|
{
|
|
struct fn_raw_data raw_data;
|
|
|
|
if (next_node->orig_next_node)
|
|
next_node = next_node->orig_next_node;
|
|
|
|
initialize_raw_data(&raw_data);
|
|
raw_data.context = next_node->context;
|
|
raw_data.orig_decl_str = next_node->decl_name;
|
|
raw_data.orig_num = next_node->num;
|
|
raw_data.hash = next_node->hash;
|
|
|
|
return get_size_overflow_hash_entry(&raw_data) != NULL;
|
|
}
|
|
|
|
// Propagate the size_overflow marks up the use-def chains
|
|
static bool has_marked_child(next_interesting_function_t next_node)
|
|
{
|
|
bool ret = false;
|
|
unsigned int i;
|
|
next_interesting_function_t child;
|
|
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
if (VEC_empty(next_interesting_function_t, next_node->children))
|
|
return false;
|
|
FOR_EACH_VEC_ELT(next_interesting_function_t, next_node->children, i, child) {
|
|
#else
|
|
FOR_EACH_VEC_SAFE_ELT(next_node->children, i, child) {
|
|
#endif
|
|
if (marked_fn(child) || already_in_the_hashtable(child))
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Set YES_SO_MARK on the function, its orig node and children if:
|
|
* * the function or its orig node or one of its children is in the hash table already
|
|
* * the function's orig node is marked with YES_SO_MARK or ASM_STMT_SO_MARK
|
|
* * one of the children is marked with YES_SO_MARK or ASM_STMT_SO_MARK
|
|
*/
|
|
static bool set_fn_so_mark(next_interesting_function_t next_node)
|
|
{
|
|
bool so_fn, so_hashtable, so_child;
|
|
|
|
so_hashtable = already_in_the_hashtable(next_node);
|
|
so_fn = marked_fn(next_node);
|
|
so_child = has_marked_child(next_node);
|
|
|
|
if (!so_fn && !so_hashtable && !so_child)
|
|
return false;
|
|
set_yes_so_mark(next_node);
|
|
return true;
|
|
}
|
|
|
|
// Determine if any of the function pointer targets have data flow between the return value and one of the arguments
|
|
static next_interesting_function_t get_same_not_ret_child(next_interesting_function_t parent)
|
|
{
|
|
unsigned int i;
|
|
next_interesting_function_t child;
|
|
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
if (VEC_empty(next_interesting_function_t, parent->children))
|
|
return NULL;
|
|
FOR_EACH_VEC_ELT(next_interesting_function_t, parent->children, i, child) {
|
|
#else
|
|
FOR_EACH_VEC_SAFE_ELT(parent->children, i, child) {
|
|
#endif
|
|
if (child->num == 0)
|
|
continue;
|
|
if (strcmp(parent->decl_name, child->decl_name))
|
|
continue;
|
|
if (!strcmp(child->context, "fndecl"))
|
|
return child;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Trace a return value of function pointer type back to an argument via a concrete function
|
|
fnptr 0 && fn 0 && (fn 0 -> fn 2) => fnptr 2 */
|
|
static void search_missing_fptr_arg(next_interesting_function_t parent)
|
|
{
|
|
next_interesting_function_t child;
|
|
unsigned int i;
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
VEC(next_interesting_function_t, heap) *new_children = NULL;
|
|
#else
|
|
vec<next_interesting_function_t, va_heap, vl_embed> *new_children = NULL;
|
|
#endif
|
|
|
|
if (parent->num != 0)
|
|
return;
|
|
if (!strcmp(parent->context, "fndecl"))
|
|
return;
|
|
if (!strncmp(parent->context, "vardecl", sizeof("vardecl") - 1))
|
|
return;
|
|
|
|
// fnptr 0 && fn 0
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
if (VEC_empty(next_interesting_function_t, parent->children))
|
|
return;
|
|
FOR_EACH_VEC_ELT(next_interesting_function_t, parent->children, i, child) {
|
|
#else
|
|
FOR_EACH_VEC_SAFE_ELT(parent->children, i, child) {
|
|
#endif
|
|
next_interesting_function_t cur_next_node, tracked_fn;
|
|
|
|
if (child->num != 0)
|
|
continue;
|
|
// (fn 0 -> fn 2)
|
|
tracked_fn = get_same_not_ret_child(child);
|
|
if (!tracked_fn)
|
|
continue;
|
|
|
|
// fn 2 => fnptr 2
|
|
for (cur_next_node = global_next_interesting_function[parent->hash]; cur_next_node; cur_next_node = cur_next_node->next) {
|
|
if (cur_next_node->num != tracked_fn->num)
|
|
continue;
|
|
|
|
if (strcmp(parent->decl_name, cur_next_node->decl_name))
|
|
continue;
|
|
|
|
if (!has_next_interesting_function_vec(parent, cur_next_node)) {
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
VEC_safe_push(next_interesting_function_t, heap, new_children, cur_next_node);
|
|
#else
|
|
vec_safe_push(new_children, cur_next_node);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#if BUILDING_GCC_VERSION == 4005
|
|
if (VEC_empty(next_interesting_function_t, new_children))
|
|
return;
|
|
FOR_EACH_VEC_ELT(next_interesting_function_t, new_children, i, child)
|
|
VEC_safe_push(next_interesting_function_t, heap, parent->children, child);
|
|
#elif BUILDING_GCC_VERSION <= 4007
|
|
VEC_safe_splice(next_interesting_function_t, heap, parent->children, new_children);
|
|
#else
|
|
vec_safe_splice(parent->children, new_children);
|
|
#endif
|
|
}
|
|
|
|
static void walk_so_marked_fns(next_interesting_function_set *visited, next_interesting_function_t parent)
|
|
{
|
|
unsigned int i;
|
|
next_interesting_function_t child;
|
|
|
|
gcc_assert(parent);
|
|
if (!set_fn_so_mark(parent))
|
|
return;
|
|
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
if (VEC_empty(next_interesting_function_t, parent->children))
|
|
return;
|
|
FOR_EACH_VEC_ELT(next_interesting_function_t, parent->children, i, child) {
|
|
#else
|
|
FOR_EACH_VEC_SAFE_ELT(parent->children, i, child) {
|
|
#endif
|
|
set_yes_so_mark(child);
|
|
|
|
if (!pointer_set_insert(visited, child))
|
|
walk_so_marked_fns(visited, child);
|
|
}
|
|
}
|
|
|
|
// Do a depth-first recursive dump of the next_interesting_function_t children vector
|
|
static void print_missing_functions(next_interesting_function_set *visited, next_interesting_function_t parent)
|
|
{
|
|
unsigned int i;
|
|
next_interesting_function_t child;
|
|
|
|
gcc_assert(parent);
|
|
gcc_assert(parent->marked != NO_SO_MARK);
|
|
print_missing_function(parent);
|
|
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
if (VEC_empty(next_interesting_function_t, parent->children))
|
|
return;
|
|
FOR_EACH_VEC_ELT(next_interesting_function_t, parent->children, i, child) {
|
|
#else
|
|
FOR_EACH_VEC_SAFE_ELT(parent->children, i, child) {
|
|
#endif
|
|
gcc_assert(child->marked != NO_SO_MARK);
|
|
if (!pointer_set_insert(visited, child))
|
|
print_missing_functions(visited, child);
|
|
}
|
|
}
|
|
|
|
// Set YES_SO_MARK on functions that will be emitted into the hash table
|
|
static void search_so_marked_fns(void)
|
|
{
|
|
|
|
unsigned int i;
|
|
next_interesting_function_set *visited;
|
|
|
|
visited = next_interesting_function_pointer_set_create();
|
|
for (i = 0; i < GLOBAL_NIFN_LEN; i++) {
|
|
next_interesting_function_t cur_global;
|
|
|
|
for (cur_global = global_next_interesting_function[i]; cur_global; cur_global = cur_global->next) {
|
|
if (cur_global->marked != NO_SO_MARK && !pointer_set_insert(visited, cur_global))
|
|
walk_so_marked_fns(visited, cur_global);
|
|
}
|
|
}
|
|
pointer_set_destroy(visited);
|
|
}
|
|
|
|
static void walk_marked_functions(next_interesting_function_set *visited, next_interesting_function_t parent)
|
|
{
|
|
unsigned int i;
|
|
next_interesting_function_t child;
|
|
|
|
if (pointer_set_insert(visited, parent))
|
|
return;
|
|
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
if (VEC_empty(next_interesting_function_t, parent->children))
|
|
return;
|
|
FOR_EACH_VEC_ELT(next_interesting_function_t, parent->children, i, child) {
|
|
#else
|
|
FOR_EACH_VEC_SAFE_ELT(parent->children, i, child) {
|
|
#endif
|
|
switch (parent->based_decl) {
|
|
case SO_FIELD:
|
|
child->based_decl = SO_FIELD;
|
|
gcc_assert(child->based_decl != SO_FUNCTION_POINTER);
|
|
break;
|
|
case SO_FUNCTION_POINTER:
|
|
child->based_decl = SO_FUNCTION_POINTER;
|
|
gcc_assert(child->based_decl != SO_FIELD);
|
|
break;
|
|
case SO_FUNCTION:
|
|
case SO_VAR:
|
|
break;
|
|
default:
|
|
gcc_unreachable();
|
|
}
|
|
|
|
walk_marked_functions(visited, child);
|
|
}
|
|
}
|
|
|
|
static void set_based_decl(void)
|
|
{
|
|
unsigned int i;
|
|
next_interesting_function_set *visited;
|
|
|
|
visited = next_interesting_function_pointer_set_create();
|
|
for (i = 0; i < GLOBAL_NIFN_LEN; i++) {
|
|
next_interesting_function_t cur;
|
|
|
|
for (cur = global_next_interesting_function[i]; cur; cur = cur->next) {
|
|
if (cur->marked == ASM_STMT_SO_MARK && !pointer_set_contains(visited, cur))
|
|
walk_marked_functions(visited, cur);
|
|
}
|
|
}
|
|
pointer_set_destroy(visited);
|
|
}
|
|
|
|
// Print functions missing from the hash table
|
|
static void print_so_marked_fns(void)
|
|
{
|
|
unsigned int i;
|
|
next_interesting_function_set *visited;
|
|
|
|
visited = next_interesting_function_pointer_set_create();
|
|
for (i = 0; i < GLOBAL_NIFN_LEN; i++) {
|
|
next_interesting_function_t cur_global;
|
|
|
|
for (cur_global = global_next_interesting_function[i]; cur_global; cur_global = cur_global->next) {
|
|
if (cur_global->marked != NO_SO_MARK && !pointer_set_insert(visited, cur_global))
|
|
print_missing_functions(visited, cur_global);
|
|
}
|
|
}
|
|
pointer_set_destroy(visited);
|
|
}
|
|
|
|
void __weak check_global_variables(next_interesting_function_t cur_global __unused) {}
|
|
|
|
static void global_vars_and_fptrs(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (!in_lto_p)
|
|
return;
|
|
|
|
// Collect vardecls and funtions reachable by function pointers
|
|
for (i = 0; i < GLOBAL_NIFN_LEN; i++) {
|
|
next_interesting_function_t cur_global;
|
|
|
|
for (cur_global = global_next_interesting_function[i]; cur_global; cur_global = cur_global->next) {
|
|
check_global_variables(cur_global);
|
|
search_missing_fptr_arg(cur_global);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void print_parent_child(next_interesting_function_set *visited, next_interesting_function_t parent)
|
|
{
|
|
unsigned int i;
|
|
next_interesting_function_t child;
|
|
|
|
#if BUILDING_GCC_VERSION <= 4007
|
|
if (VEC_empty(next_interesting_function_t, parent->children))
|
|
return;
|
|
FOR_EACH_VEC_ELT(next_interesting_function_t, parent->children, i, child) {
|
|
#else
|
|
FOR_EACH_VEC_SAFE_ELT(parent->children, i, child) {
|
|
#endif
|
|
fprintf(stderr, " PARENT: decl: %s-%u context: %s %p\n", parent->decl_name, parent->num, parent->context, parent);
|
|
fprintf(stderr, " \tCHILD: decl: %s-%u context: %s %p\n", child->decl_name, child->num, child->context, child);
|
|
|
|
if (!pointer_set_insert(visited, child))
|
|
print_parent_child(visited, child);
|
|
}
|
|
}
|
|
|
|
static void print_data_flow(void)
|
|
{
|
|
unsigned int i;
|
|
next_interesting_function_set *visited;
|
|
|
|
if (!in_lto_p)
|
|
return;
|
|
|
|
visited = next_interesting_function_pointer_set_create();
|
|
for (i = 0; i < GLOBAL_NIFN_LEN; i++) {
|
|
next_interesting_function_t cur_global;
|
|
|
|
for (cur_global = global_next_interesting_function[i]; cur_global; cur_global = cur_global->next) {
|
|
if (cur_global->marked == NO_SO_MARK || pointer_set_insert(visited, cur_global))
|
|
continue;
|
|
|
|
fprintf(stderr, "Data flow: decl: %s-%u context: %s %p\n", cur_global->decl_name, cur_global->num, cur_global->context, cur_global);
|
|
|
|
print_parent_child(visited, cur_global);
|
|
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
pointer_set_destroy(visited);
|
|
}
|
|
|
|
static void set_so_fns(void)
|
|
{
|
|
do {
|
|
global_changed = false;
|
|
search_so_marked_fns();
|
|
} while (global_changed);
|
|
|
|
print_data_flow();
|
|
}
|
|
|
|
// Print all missing interesting functions
|
|
static unsigned int size_overflow_execute(void)
|
|
{
|
|
if (flag_lto && !in_lto_p)
|
|
return 0;
|
|
|
|
global_vars_and_fptrs();
|
|
|
|
set_so_fns();
|
|
set_based_decl();
|
|
print_so_marked_fns();
|
|
|
|
if (in_lto_p) {
|
|
fprintf(stderr, "%s: SIZE_OVERFLOW EXECUTE\n", __func__);
|
|
print_global_next_interesting_functions();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Omit the IPA/LTO callbacks until https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61311 gets fixed (license concerns)
|
|
#if BUILDING_GCC_VERSION >= 4008
|
|
void __weak size_overflow_write_summary(void) {}
|
|
void __weak size_overflow_write_optimization_summary(void) {}
|
|
#elif BUILDING_GCC_VERSION >= 4006
|
|
void __weak size_overflow_write_summary(cgraph_node_set set __unused, varpool_node_set vset __unused) {}
|
|
void __weak size_overflow_write_optimization_summary(cgraph_node_set set __unused, varpool_node_set vset __unused) {}
|
|
#else
|
|
void __weak size_overflow_write_summary(cgraph_node_set set __unused) {}
|
|
void __weak size_overflow_write_optimization_summary(cgraph_node_set set __unused) {}
|
|
#endif
|
|
|
|
void __weak size_overflow_read_summary(void);
|
|
void __weak size_overflow_read_optimization_summary(void);
|
|
|
|
#define PASS_NAME size_overflow
|
|
|
|
#define NO_STMT_FIXUP
|
|
#define NO_VARIABLE_TRANSFORM
|
|
#define NO_GATE
|
|
|
|
#include "gcc-generate-ipa-pass.h"
|