+index 0000000..560cd7b
+--- /dev/null
++++ b/tools/gcc/size_overflow_plugin/size_overflow_hash_aux.data
+@@ -0,0 +1,92 @@
++spa_set_aux_vdevs_746 spa_set_aux_vdevs 3 746 NULL
++zfs_lookup_2144 zfs_lookup 0 2144 NULL
++mappedread_2627 mappedread 2 2627 NULL
++vdev_disk_dio_alloc_2957 vdev_disk_dio_alloc 1 2957 NULL
++nv_alloc_pushpage_spl_4286 nv_alloc_pushpage_spl 2 4286 NULL
++zpl_xattr_get_4574 zpl_xattr_get 0 4574 NULL
++sa_replace_all_by_template_5699 sa_replace_all_by_template 3 5699 NULL
++dmu_write_6048 dmu_write 4-3 6048 NULL
++dmu_buf_hold_array_6095 dmu_buf_hold_array 4-3 6095 NULL
++update_pages_6225 update_pages 2-3 6225 NULL
++bio_nr_pages_7117 bio_nr_pages 0-2 7117 NULL
++dmu_buf_hold_array_by_bonus_8562 dmu_buf_hold_array_by_bonus 3-2 8562 NULL
++zpios_dmu_write_8858 zpios_dmu_write 4-5 8858 NULL
++ddi_copyout_9401 ddi_copyout 3 9401 NULL
++avl_numnodes_12384 avl_numnodes 0 12384 NULL
++dmu_write_uio_dnode_12473 dmu_write_uio_dnode 3 12473 NULL
++dmu_xuio_init_12866 dmu_xuio_init 2 12866 NULL
++zpl_read_common_14389 zpl_read_common 0 14389 NULL
++dmu_snapshot_realname_14632 dmu_snapshot_realname 4 14632 NULL
++kmem_alloc_debug_14852 kmem_alloc_debug 1 14852 NULL
++kmalloc_node_nofail_15151 kmalloc_node_nofail 1 15151 NULL
++dmu_write_uio_16351 dmu_write_uio 4 16351 NULL
++zfs_log_write_16524 zfs_log_write 6-5 16524 NULL
++sa_build_layouts_16910 sa_build_layouts 3 16910 NULL
++dsl_dir_namelen_17053 dsl_dir_namelen 0 17053 NULL
++kcopy_copy_to_user_17336 kcopy_copy_to_user 5 17336 NULL
++sa_add_layout_entry_17507 sa_add_layout_entry 3 17507 NULL
++sa_attr_table_setup_18029 sa_attr_table_setup 3 18029 NULL
++uiocopy_18680 uiocopy 2 18680 NULL
++dmu_buf_hold_array_by_dnode_19125 dmu_buf_hold_array_by_dnode 2-3 19125 NULL
++zpl_acl_from_xattr_21141 zpl_acl_from_xattr 2 21141 NULL
++dsl_pool_tx_assign_init_22518 dsl_pool_tx_assign_init 2 22518 NULL
++nvlist_lookup_byte_array_22527 nvlist_lookup_byte_array 0 22527 NULL
++sa_replace_all_by_template_locked_22533 sa_replace_all_by_template_locked 3 22533 NULL
++tsd_hash_table_init_22559 tsd_hash_table_init 1 22559 NULL
++spa_vdev_remove_aux_23966 spa_vdev_remove_aux 4 23966 NULL
++zpl_xattr_acl_set_access_24129 zpl_xattr_acl_set_access 4 24129 NULL
++dmu_assign_arcbuf_24622 dmu_assign_arcbuf 2 24622 NULL
++zap_lookup_norm_25166 zap_lookup_norm 9 25166 NULL
++dmu_prealloc_25456 dmu_prealloc 4-3 25456 NULL
++kmalloc_nofail_26347 kmalloc_nofail 1 26347 NULL
++zfsctl_snapshot_zpath_27578 zfsctl_snapshot_zpath 2 27578 NULL
++zpios_dmu_read_30015 zpios_dmu_read 4-5 30015 NULL
++splat_write_30943 splat_write 3 30943 NULL
++zpl_xattr_get_sa_31183 zpl_xattr_get_sa 0 31183 NULL
++dmu_read_uio_31467 dmu_read_uio 4 31467 NULL
++zfs_replay_fuids_31479 zfs_replay_fuids 4 31479 NULL
++spa_history_log_to_phys_31632 spa_history_log_to_phys 0-1 31632 NULL
++__zpl_xattr_get_32601 __zpl_xattr_get 0 32601 NULL
++proc_copyout_string_34049 proc_copyout_string 2 34049 NULL
++nv_alloc_sleep_spl_34544 nv_alloc_sleep_spl 2 34544 NULL
++nv_alloc_nosleep_spl_34761 nv_alloc_nosleep_spl 2 34761 NULL
++zap_leaf_array_match_36922 zap_leaf_array_match 4 36922 NULL
++copyinstr_36980 copyinstr 3 36980 NULL
++zpl_xattr_acl_set_default_37864 zpl_xattr_acl_set_default 4 37864 NULL
++splat_read_38116 splat_read 3 38116 NULL
++sa_setup_38756 sa_setup 4 38756 NULL
++vdev_disk_physio_39898 vdev_disk_physio 3 39898 NULL
++arc_buf_size_39982 arc_buf_size 0 39982 NULL
++kzalloc_nofail_40719 kzalloc_nofail 1 40719 NULL
++fuidstr_to_sid_40777 fuidstr_to_sid 4 40777 NULL
++vdev_raidz_matrix_reconstruct_40852 vdev_raidz_matrix_reconstruct 2-3 40852 NULL
++sa_find_layout_40892 sa_find_layout 4 40892 NULL
++zpl_xattr_get_dir_41918 zpl_xattr_get_dir 0 41918 NULL
++zfs_sa_get_xattr_42600 zfs_sa_get_xattr 0 42600 NULL
++zpl_xattr_acl_set_42808 zpl_xattr_acl_set 4 42808 NULL
++xdr_dec_array_43091 xdr_dec_array 5 43091 NULL
++dsl_dataset_namelen_43136 dsl_dataset_namelen 0 43136 NULL
++kcopy_write_43683 kcopy_write 3 43683 NULL
++uiomove_44355 uiomove 2 44355 NULL
++dmu_read_44418 dmu_read 4-3 44418 NULL
++ddi_copyin_44846 ddi_copyin 3 44846 NULL
++kcopy_do_get_45061 kcopy_do_get 5 45061 NULL
++copyin_45945 copyin 3 45945 NULL
++zil_itx_create_46555 zil_itx_create 2 46555 NULL
++dmu_write_uio_dbuf_48064 dmu_write_uio_dbuf 3 48064 NULL
++blk_rq_pos_48233 blk_rq_pos 0 48233 NULL
++spa_history_write_49650 spa_history_write 3 49650 NULL
++kcopy_copy_pages_to_user_49823 kcopy_copy_pages_to_user 3-4 49823 NULL
++zfs_log_write_50162 zfs_log_write 6-5 50162 NULL
++i_fm_alloc_51038 i_fm_alloc 2 51038 NULL
++copyout_51409 copyout 3 51409 NULL
++zvol_log_write_54898 zvol_log_write 4-3 54898 NULL
++zfs_acl_node_alloc_55641 zfs_acl_node_alloc 1 55641 NULL
++get_nvlist_56685 get_nvlist 2 56685 NULL
++zprop_get_numprops_56820 zprop_get_numprops 0 56820 NULL
++splat_taskq_test4_common_59829 splat_taskq_test4_common 5 59829 NULL
++zfs_replay_domain_cnt_61399 zfs_replay_domain_cnt 0 61399 NULL
++zpios_write_61823 zpios_write 3 61823 NULL
++proc_copyin_string_62019 proc_copyin_string 4 62019 NULL
++random_get_pseudo_bytes_64611 random_get_pseudo_bytes 2 64611 NULL
++zpios_read_64734 zpios_read 3 64734 NULL
+diff --git a/tools/gcc/size_overflow_plugin/size_overflow_ipa.c b/tools/gcc/size_overflow_plugin/size_overflow_ipa.c
+new file mode 100644
+index 0000000..f888b36
+--- /dev/null
++++ b/tools/gcc/size_overflow_plugin/size_overflow_ipa.c
+@@ -0,0 +1,908 @@
++/*
++ * Copyright 2011-2015 by Emese Revfy <re.emese@gmail.com>
++ * Licensed under the GPL v2, or (at your option) v3
++ *
++ * 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"
++
++static next_interesting_function_t walk_use_def_next_functions(struct pointer_set_t *visited, next_interesting_function_t next_cnodes_head, const_tree lhs);
++
++next_interesting_function_t global_next_interesting_function[GLOBAL_NIFN_LEN];
++
++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((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)
++{
++ if (cur_node->marked == ASM_STMT_SO_MARK)
++ return false;
++ if (num != CANNOT_FIND_ARG && cur_node->num != num)
++ return false;
++ if (strcmp(cur_node->context, context))
++ return false;
++ return !strcmp(cur_node->decl_name, decl_name);
++}
++
++// Return the type name for a function pointer (or "fielddecl" if the type has no name), otherwise either "vardecl" or "fndecl"
++static const char* get_decl_context(const_tree decl)
++{
++ const char *context;
++
++ if (TREE_CODE(decl) == VAR_DECL)
++ return "vardecl";
++ if (TREE_CODE(decl) == FUNCTION_DECL)
++ return "fndecl";
++
++ gcc_assert(TREE_CODE(decl) == FIELD_DECL);
++ context = get_type_name_from_field(decl);
++
++ if (!context)
++ return "fielddecl";
++ return context;
++}
++
++/* Find the function with the specified argument in the list
++ * * if marked is ASM_STMT_SO_MARK or YES_SO_MARK then filter accordingly
++ * * if num is CANNOT_FIND_ARG then ignore it
++ */
++next_interesting_function_t get_global_next_interesting_function_entry(const char *decl_name, const char *context, unsigned int hash, unsigned int num, enum size_overflow_mark marked)
++{
++ next_interesting_function_t cur_node, head;
++
++ head = global_next_interesting_function[hash];
++ for (cur_node = head; cur_node; cur_node = cur_node->next) {
++ if ((marked == ASM_STMT_SO_MARK || marked == YES_SO_MARK) && cur_node->marked != marked)
++ continue;
++ if (compare_next_interesting_functions(cur_node, decl_name, context, num))
++ return cur_node;
++ }
++ return NULL;
++}
++
++next_interesting_function_t get_global_next_interesting_function_entry_with_hash(const_tree decl, const char *decl_name, unsigned int num, enum size_overflow_mark marked)
++{
++ const char *context;
++ unsigned int hash;
++
++ hash = get_decl_hash(decl, decl_name);
++ if (hash == NO_HASH)
++ return NULL;
++
++ context = get_decl_context(decl);
++ return get_global_next_interesting_function_entry(decl_name, context, hash, num, marked);
++}
++
++static bool is_vararg(const_tree fn, unsigned int num)
++{
++ const_tree fn_type, last, type;
++ tree arg_list;
++
++ if (num == 0)
++ return false;
++ if (fn == NULL_TREE)
++ return false;
++ if (TREE_CODE(fn) != FUNCTION_DECL)
++ return false;
++
++ fn_type = TREE_TYPE(fn);
++ if (fn_type == NULL_TREE)
++ return false;
++
++ arg_list = TYPE_ARG_TYPES(fn_type);
++ if (arg_list == NULL_TREE)
++ return false;
++ last = TREE_VALUE(tree_last(arg_list));
++
++ if (TREE_CODE_CLASS(TREE_CODE(last)) == tcc_type)
++ type = last;
++ else
++ type = TREE_TYPE(last);
++
++ gcc_assert(type != NULL_TREE);
++ if (type == void_type_node)
++ return false;
++
++ return num >= (unsigned int)list_length(arg_list);
++}
++
++next_interesting_function_t create_new_next_interesting_entry(const char *decl_name, const char *context, unsigned int hash, unsigned int num, enum size_overflow_mark marked, next_interesting_function_t orig_next_node)
++{
++ next_interesting_function_t new_node;
++
++ new_node = (next_interesting_function_t)xmalloc(sizeof(*new_node));
++ new_node->decl_name = xstrdup(decl_name);
++ gcc_assert(context);
++ new_node->context = xstrdup(context);
++ new_node->hash = hash;
++ new_node->num = num;
++ new_node->next = NULL;
++ new_node->children = NULL;
++ new_node->marked = marked;
++ new_node->orig_next_node = orig_next_node;
++ return new_node;
++}
++
++// Create the main data structure
++next_interesting_function_t create_new_next_interesting_decl(tree decl, const char *decl_name, unsigned int num, enum size_overflow_mark marked, next_interesting_function_t orig_next_node)
++{
++ unsigned int hash;
++ const char *context;
++ enum tree_code decl_code = TREE_CODE(decl);
++
++ gcc_assert(decl_code == FIELD_DECL || decl_code == FUNCTION_DECL || decl_code == VAR_DECL);
++
++ if (is_vararg(decl, num))
++ return NULL;
++
++ hash = get_decl_hash(decl, decl_name);
++ if (hash == NO_HASH)
++ return NULL;
++
++ gcc_assert(num <= MAX_PARAM);
++ // Clones must have an orig_next_node
++ gcc_assert(!made_by_compiler(decl) || orig_next_node);
++
++ context = get_decl_context(decl);
++ return create_new_next_interesting_entry(decl_name, context, hash, num, marked, 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(const_tree clone_fndecl, unsigned int num, enum size_overflow_mark marked)
++{
++ next_interesting_function_t orig_next_node;
++ tree decl;
++ unsigned int orig_num;
++ enum tree_code decl_code;
++ const char *decl_name;
++
++ decl = get_orig_fndecl(clone_fndecl);
++ decl_code = TREE_CODE(decl);
++
++ if (decl_code == FIELD_DECL || decl_code == VAR_DECL)
++ orig_num = num;
++ else
++ orig_num = get_correct_argnum(clone_fndecl, decl, num);
++
++ // Skip over ISRA.162 parm decls
++ if (orig_num == CANNOT_FIND_ARG)
++ return NULL;
++
++ decl_name = get_orig_decl_name(decl);
++ orig_next_node = get_global_next_interesting_function_entry_with_hash(decl, decl_name, orig_num, NO_SO_MARK);
++ if (orig_next_node)
++ return orig_next_node;
++
++ orig_next_node = create_new_next_interesting_decl(decl, decl_name, orig_num, marked, NULL);
++ gcc_assert(orig_next_node);
++
++ 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(tree decl, unsigned int num, enum size_overflow_mark marked, next_interesting_function_t orig_next_node)
++{
++ next_interesting_function_t cur_next_cnode;
++ const char *decl_name = DECL_NAME_POINTER(decl);
++
++ cur_next_cnode = get_global_next_interesting_function_entry_with_hash(decl, decl_name, num, marked);
++ if (cur_next_cnode)
++ goto out;
++
++ if (!orig_next_node && made_by_compiler(decl)) {
++ orig_next_node = create_orig_next_node_for_a_clone(decl, num, marked);
++ if (!orig_next_node)
++ return NULL;
++ }
++
++ cur_next_cnode = create_new_next_interesting_decl(decl, decl_name, num, marked, orig_next_node);
++ if (!cur_next_cnode)
++ return NULL;
++
++ add_to_global_next_interesting_function(cur_next_cnode);
++out:
++ if (cur_next_cnode->marked != marked && cur_next_cnode->marked == YES_SO_MARK)
++ return cur_next_cnode;
++ gcc_assert(cur_next_cnode->marked == marked);
++ return cur_next_cnode;
++}
++
++static bool has_next_interesting_function_chain_node(next_interesting_function_t next_cnodes_head, const_tree decl, unsigned int num)
++{
++ next_interesting_function_t cur_node;
++ const char *context, *decl_name;
++
++ decl_name = DECL_NAME_POINTER(decl);
++ context = get_decl_context(decl);
++ for (cur_node = next_cnodes_head; cur_node; cur_node = cur_node->next) {
++ if (compare_next_interesting_functions(cur_node, decl_name, context, num))
++ return true;
++ }
++ return false;
++}
++
++static next_interesting_function_t handle_function(next_interesting_function_t next_cnodes_head, tree fndecl, const_tree arg)
++{
++ unsigned int num;
++ 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))
++ return next_cnodes_head;
++
++ // convert arg into its position
++ if (arg == NULL_TREE)
++ num = 0;
++ else
++ num = find_arg_number_tree(arg, fndecl);
++ if (num == CANNOT_FIND_ARG)
++ return next_cnodes_head;
++
++ if (has_next_interesting_function_chain_node(next_cnodes_head, fndecl, num))
++ return next_cnodes_head;
++
++ if (made_by_compiler(fndecl)) {
++ orig_next_node = create_orig_next_node_for_a_clone(fndecl, num, NO_SO_MARK);
++ if (!orig_next_node)
++ return next_cnodes_head;
++ } else
++ orig_next_node = NULL;
++ new_node = create_new_next_interesting_decl(fndecl, DECL_NAME_POINTER(fndecl), num, NO_SO_MARK, orig_next_node);
++ if (!new_node)
++ return next_cnodes_head;
++ new_node->next = next_cnodes_head;
++ return new_node;
++}
++
++static next_interesting_function_t walk_use_def_next_functions_phi(struct pointer_set_t *visited, next_interesting_function_t next_cnodes_head, const_tree result)
++{
++ gimple phi = get_def_stmt(result);
++ unsigned int i, n = gimple_phi_num_args(phi);
++
++ pointer_set_insert(visited, phi);
++ for (i = 0; i < n; i++) {
++ tree arg = gimple_phi_arg_def(phi, i);
++
++ next_cnodes_head = walk_use_def_next_functions(visited, next_cnodes_head, arg);
++ }
++
++ return next_cnodes_head;
++}
++
++static next_interesting_function_t walk_use_def_next_functions_binary(struct pointer_set_t *visited, next_interesting_function_t next_cnodes_head, const_tree lhs)
++{
++ gimple def_stmt = get_def_stmt(lhs);
++ tree rhs1, rhs2;
++
++ rhs1 = gimple_assign_rhs1(def_stmt);
++ rhs2 = gimple_assign_rhs2(def_stmt);
++
++ next_cnodes_head = walk_use_def_next_functions(visited, next_cnodes_head, rhs1);
++ return walk_use_def_next_functions(visited, next_cnodes_head, rhs2);
++}
++
++next_interesting_function_t __attribute__((weak)) handle_function_ptr_ret(struct pointer_set_t *visited __unused, next_interesting_function_t next_cnodes_head, const_tree fn_ptr __unused)
++{
++ return next_cnodes_head;
++}
++
++/* Find all functions that influence lhs
++ *
++ * Encountered functions are added to the children vector (next_interesting_function_t).
++ */
++static next_interesting_function_t walk_use_def_next_functions(struct pointer_set_t *visited, next_interesting_function_t next_cnodes_head, const_tree lhs)
++{
++ const_gimple def_stmt;
++
++ if (skip_types(lhs))
++ return next_cnodes_head;
++
++ if (TREE_CODE(lhs) == PARM_DECL)
++ return handle_function(next_cnodes_head, current_function_decl, lhs);
++
++ if (TREE_CODE(lhs) != SSA_NAME)
++ return next_cnodes_head;
++
++ def_stmt = get_def_stmt(lhs);
++ if (!def_stmt)
++ return next_cnodes_head;
++
++ if (pointer_set_insert(visited, def_stmt))
++ return next_cnodes_head;
++
++ switch (gimple_code(def_stmt)) {
++ case GIMPLE_NOP:
++ return walk_use_def_next_functions(visited, next_cnodes_head, SSA_NAME_VAR(lhs));
++ case GIMPLE_ASM:
++ if (is_size_overflow_asm(def_stmt))
++ return walk_use_def_next_functions(visited, next_cnodes_head, get_size_overflow_asm_input(def_stmt));
++ return next_cnodes_head;
++ case GIMPLE_CALL: {
++ tree fndecl = gimple_call_fndecl(def_stmt);
++
++ if (fndecl != NULL_TREE)
++ return handle_function(next_cnodes_head, fndecl, NULL_TREE);
++ fndecl = gimple_call_fn(def_stmt);
++ return handle_function_ptr_ret(visited, next_cnodes_head, fndecl);
++ }
++ case GIMPLE_PHI:
++ return walk_use_def_next_functions_phi(visited, next_cnodes_head, lhs);
++ case GIMPLE_ASSIGN:
++ switch (gimple_num_ops(def_stmt)) {
++ case 2:
++ return walk_use_def_next_functions(visited, next_cnodes_head, gimple_assign_rhs1(def_stmt));
++ case 3:
++ return walk_use_def_next_functions_binary(visited, next_cnodes_head, lhs);
++ }
++ 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(const_tree node)
++{
++ struct pointer_set_t *visited;
++ next_interesting_function_t next_cnodes_head;
++
++ visited = pointer_set_create();
++ next_cnodes_head = walk_use_def_next_functions(visited, NULL, node);
++ pointer_set_destroy(visited);
++
++ return 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 __attribute__((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) {
++ next_interesting_function_t next, child;
++
++ next = cur->next;
++
++ child = get_global_next_interesting_function_entry(cur->decl_name, cur->context, cur->hash, cur->num, NO_SO_MARK);
++ 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 __attribute__((weak)) get_and_create_next_node_from_global_next_nodes_fnptr(const_tree fn_ptr __unused, unsigned int num __unused, enum size_overflow_mark marked __unused)
++{
++ return NULL;
++}
++
++static next_interesting_function_t create_parent_next_cnode(const_gimple stmt, unsigned int num)
++{
++ switch (gimple_code(stmt)) {
++ case GIMPLE_ASM:
++ return get_and_create_next_node_from_global_next_nodes(current_function_decl, num, ASM_STMT_SO_MARK, NULL);
++ case GIMPLE_CALL: {
++ tree decl = gimple_call_fndecl(stmt);
++
++ if (decl != NULL_TREE)
++ return get_and_create_next_node_from_global_next_nodes(decl, num, NO_SO_MARK, NULL);
++ decl = gimple_call_fn(stmt);
++ return get_and_create_next_node_from_global_next_nodes_fnptr(decl, num, NO_SO_MARK);
++ }
++ case GIMPLE_RETURN:
++ return get_and_create_next_node_from_global_next_nodes(current_function_decl, num, NO_SO_MARK, 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, unsigned int num)
++{
++ const_tree start_var;
++ next_interesting_function_t children_next_cnode, parent_next_cnode;
++
++ switch (gimple_code(stmt)) {
++ case GIMPLE_ASM:
++ if (!is_size_overflow_insert_check_asm(stmt))
++ return;
++ start_var = get_size_overflow_asm_input(stmt);
++ gcc_assert(start_var != NULL_TREE);
++ break;
++ case GIMPLE_CALL:
++ start_var = gimple_call_arg(stmt, num - 1);
++ break;
++ case GIMPLE_RETURN:
++ start_var = gimple_return_retval(stmt);
++ if (start_var == NULL_TREE)
++ return;
++ break;
++ default:
++ debug_gimple_stmt((gimple)stmt);
++ gcc_unreachable();
++ }
++
++ if (skip_types(start_var))
++ return;
++
++ // handle intentional MARK_TURN_OFF
++ if (check_intentional_asm(stmt, num) == 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);
++ collect_data_for_execute(parent_next_cnode, children_next_cnode);
++}
++
++// 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_ALL_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);
++
++ switch (gimple_code(stmt)) {
++ case GIMPLE_RETURN:
++ case GIMPLE_ASM:
++ collect_all_possible_size_overflow_fns(stmt, 0);
++ break;
++ case GIMPLE_CALL: {
++ unsigned int i, len;
++ tree fndecl = gimple_call_fndecl(stmt);
++
++ if (fndecl != NULL_TREE && DECL_BUILT_IN(fndecl))
++ break;
++
++ len = gimple_call_num_args(stmt);
++ for (i = 0; i < len; i++)
++ collect_all_possible_size_overflow_fns(stmt, i + 1);
++ 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;
++ const_tree decl;
++ const char *src_name, *src_context;
++
++ decl = NODE_DECL(src);
++ src_name = DECL_NAME_POINTER(decl);
++ src_context = get_decl_context(decl);
++
++ head = get_global_next_interesting_function_entry_with_hash(decl, src_name, NONE_ARGNUM, NO_SO_MARK);
++ if (!head)
++ return;
++
++ for (cur = head; cur; cur = cur->next) {
++ unsigned int new_argnum;
++ next_interesting_function_t orig_next_node, next_node;
++ bool dst_clone;
++
++ if (!compare_next_interesting_functions(cur, src_name, src_context, CANNOT_FIND_ARG))
++ continue;
++
++ dst_clone = made_by_compiler(NODE_DECL(dst));
++ if (!dst_clone)
++ 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;
++
++ new_argnum = get_correct_argnum_fndecl(NODE_DECL(src), NODE_DECL(dst), cur->num);
++ if (new_argnum == CANNOT_FIND_ARG)
++ continue;
++
++ next_node = create_new_next_interesting_decl(NODE_DECL(dst), cgraph_node_name(dst), new_argnum, cur->marked, 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)
++{
++ next_node->marked = YES_SO_MARK;
++ // Mark the orig decl as well if it's a clone
++ if (next_node->orig_next_node)
++ next_node->orig_next_node->marked = YES_SO_MARK;
++}
++
++// Determine if the function is already in the hash table
++static bool is_marked_fn(next_interesting_function_t next_node)
++{
++ const struct size_overflow_hash *entry;
++
++ if (next_node->marked != NO_SO_MARK)
++ return true;
++
++ if (next_node->orig_next_node)
++ entry = get_size_overflow_hash_entry(next_node->orig_next_node->hash, next_node->orig_next_node->decl_name, next_node->orig_next_node->num);
++ else
++ entry = get_size_overflow_hash_entry(next_node->hash, next_node->decl_name, next_node->num);
++ if (!entry)
++ 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 tracked_fn, cur_next_node, 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 (!strcmp(parent->context, "vardecl"))
++ 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
++ 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
++}
++
++// Do a depth-first recursive dump of the next_interesting_function_t children vector
++static void print_missing_functions(struct pointer_set_t *visited, next_interesting_function_t parent)
++{
++ unsigned int i;
++ next_interesting_function_t child;
++
++ gcc_assert(parent);
++ check_global_variables(parent);
++ search_missing_fptr_arg(parent);
++ 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
++ // Since the parent is a marked function we will set YES_SO_MARK on the children to transform them as well
++ child->marked = YES_SO_MARK;
++ if (!pointer_set_insert(visited, child))
++ print_missing_functions(visited, child);
++ }
++}
++
++void __attribute__((weak)) check_global_variables(next_interesting_function_t cur_global __unused) {}
++
++// Print all missing interesting functions
++static unsigned int size_overflow_execute(void)
++{
++ unsigned int i;
++ struct pointer_set_t *visited;
++ next_interesting_function_t cur_global;
++
++ visited = pointer_set_create();
++
++ for (i = 0; i < GLOBAL_NIFN_LEN; i++) {
++ for (cur_global = global_next_interesting_function[i]; cur_global; cur_global = cur_global->next) {
++ if (is_marked_fn(cur_global))
++ print_missing_functions(visited, cur_global);
++ }
++ }
++
++ pointer_set_destroy(visited);
++
++/* 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 __attribute__((weak)) size_overflow_write_summary_lto(void) {}
++#elif BUILDING_GCC_VERSION >= 4006
++void __attribute__((weak)) size_overflow_write_summary_lto(cgraph_node_set set __unused, varpool_node_set vset __unused) {}
++#else
++void __attribute__((weak)) size_overflow_write_summary_lto(cgraph_node_set set __unused) {}
++#endif
++
++void __attribute__((weak)) size_overflow_read_summary_lto(void) {}
++
++#if BUILDING_GCC_VERSION >= 4009
++static const struct pass_data size_overflow_functions_pass_data = {
++#else
++static struct ipa_opt_pass_d size_overflow_functions_pass = {
++ .pass = {
++#endif
++ .type = IPA_PASS,
++ .name = "size_overflow_functions",
++#if BUILDING_GCC_VERSION >= 4008
++ .optinfo_flags = OPTGROUP_NONE,
++#endif
++#if BUILDING_GCC_VERSION >= 4009
++ .has_gate = false,
++ .has_execute = true,
++#else
++ .gate = NULL,
++ .execute = size_overflow_execute,
++ .sub = NULL,
++ .next = NULL,
++ .static_pass_number = 0,
++#endif
++ .tv_id = TV_NONE,
++ .properties_required = 0,
++ .properties_provided = 0,
++ .properties_destroyed = 0,
++ .todo_flags_start = 0,
++ .todo_flags_finish = 0,
++#if BUILDING_GCC_VERSION < 4009
++ },
++ .generate_summary = size_overflow_generate_summary,
++ .write_summary = size_overflow_write_summary_lto,
++ .read_summary = size_overflow_read_summary_lto,
++#if BUILDING_GCC_VERSION >= 4006
++ .write_optimization_summary = size_overflow_write_summary_lto,
++ .read_optimization_summary = size_overflow_read_summary_lto,
++#endif
++ .stmt_fixup = NULL,
++ .function_transform_todo_flags_start = 0,
++ .function_transform = size_overflow_transform,
++ .variable_transform = NULL,
++#endif
++};
++
++#if BUILDING_GCC_VERSION >= 4009
++namespace {
++class size_overflow_functions_pass : public ipa_opt_pass_d {
++public:
++ size_overflow_functions_pass() : ipa_opt_pass_d(size_overflow_functions_pass_data,
++ g,
++ size_overflow_generate_summary,
++ size_overflow_write_summary_lto,
++ size_overflow_read_summary_lto,
++ size_overflow_write_summary_lto,
++ size_overflow_read_summary_lto,
++ NULL,
++ 0,
++ size_overflow_transform,
++ NULL) {}
++ unsigned int execute() { return size_overflow_execute(); }
++};
++}
++
++opt_pass *make_size_overflow_functions_pass(void)
++{
++ return new size_overflow_functions_pass();
++}
++#else
++struct opt_pass *make_size_overflow_functions_pass(void)
++{
++ return &size_overflow_functions_pass.pass;
++}
++#endif
+diff --git a/tools/gcc/size_overflow_plugin/size_overflow_plugin.c b/tools/gcc/size_overflow_plugin/size_overflow_plugin.c
+new file mode 100644
+index 0000000..036dc3f
+--- /dev/null
++++ b/tools/gcc/size_overflow_plugin/size_overflow_plugin.c
+@@ -0,0 +1,231 @@
++/*
++ * Copyright 2011-2015 by Emese Revfy <re.emese@gmail.com>
++ * Licensed under the GPL v2, or (at your option) v3
++ *
++ * 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"
++
++int plugin_is_GPL_compatible;
++
++tree report_size_overflow_decl;
++
++tree size_overflow_type_HI;
++tree size_overflow_type_SI;
++tree size_overflow_type_DI;
++tree size_overflow_type_TI;
++
++static struct plugin_info size_overflow_plugin_info = {
++ .version = "20150311",
++ .help = "no-size-overflow\tturn off size overflow checking\n",
++};
++
++static tree handle_size_overflow_attribute(tree *node, tree __unused name, tree args, int __unused flags, bool *no_add_attrs)
++{
++ unsigned int arg_count;
++ enum tree_code code = TREE_CODE(*node);
++
++ switch (code) {
++ case FUNCTION_DECL:
++ arg_count = type_num_arguments(TREE_TYPE(*node));
++ break;
++ case FUNCTION_TYPE:
++ case METHOD_TYPE:
++ arg_count = type_num_arguments(*node);
++ break;
++ default:
++ *no_add_attrs = true;
++ error("%s: %qE attribute only applies to functions", __func__, name);
++ return NULL_TREE;
++ }
++
++ for (; args; args = TREE_CHAIN(args)) {
++ tree position = TREE_VALUE(args);
++ if (TREE_CODE(position) != INTEGER_CST || TREE_INT_CST_LOW(position) > arg_count ) {
++ error("%s: parameter %u is outside range.", __func__, (unsigned int)TREE_INT_CST_LOW(position));
++ *no_add_attrs = true;
++ }
++ }
++ return NULL_TREE;
++}
++
++static tree handle_intentional_overflow_attribute(tree *node, tree __unused name, tree args, int __unused flags, bool *no_add_attrs)
++{
++ unsigned int arg_count;
++ enum tree_code code = TREE_CODE(*node);
++
++ switch (code) {
++ case FUNCTION_DECL:
++ arg_count = type_num_arguments(TREE_TYPE(*node));
++ break;
++ case FUNCTION_TYPE:
++ case METHOD_TYPE:
++ arg_count = type_num_arguments(*node);
++ break;
++ case FIELD_DECL:
++ return NULL_TREE;
++ default:
++ *no_add_attrs = true;
++ error("%qE attribute only applies to functions", name);
++ return NULL_TREE;
++ }
++
++ if (TREE_INT_CST_HIGH(TREE_VALUE(args)) != 0)
++ return NULL_TREE;
++
++ for (; args; args = TREE_CHAIN(args)) {
++ tree position = TREE_VALUE(args);
++ if (TREE_CODE(position) != INTEGER_CST || TREE_INT_CST_LOW(position) > arg_count ) {
++ error("%s: parameter %u is outside range.", __func__, (unsigned int)TREE_INT_CST_LOW(position));
++ *no_add_attrs = true;
++ }
++ }
++ return NULL_TREE;
++}
++
++static struct attribute_spec size_overflow_attr = {
++ .name = "size_overflow",
++ .min_length = 1,
++ .max_length = -1,
++ .decl_required = true,
++ .type_required = false,
++ .function_type_required = false,
++ .handler = handle_size_overflow_attribute,
++#if BUILDING_GCC_VERSION >= 4007
++ .affects_type_identity = false
++#endif
++};
++
++static struct attribute_spec intentional_overflow_attr = {
++ .name = "intentional_overflow",
++ .min_length = 1,
++ .max_length = -1,
++ .decl_required = true,
++ .type_required = false,
++ .function_type_required = false,
++ .handler = handle_intentional_overflow_attribute,
++#if BUILDING_GCC_VERSION >= 4007
++ .affects_type_identity = false
++#endif
++};
++
++static void register_attributes(void __unused *event_data, void __unused *data)
++{
++ register_attribute(&size_overflow_attr);
++ register_attribute(&intentional_overflow_attr);
++}
++
++static tree create_typedef(tree type, const char* ident)
++{
++ tree new_type, decl;
++
++ new_type = build_variant_type_copy(type);
++ decl = build_decl(BUILTINS_LOCATION, TYPE_DECL, get_identifier(ident), new_type);
++ DECL_ORIGINAL_TYPE(decl) = type;
++ TYPE_NAME(new_type) = decl;
++ return new_type;
++}
++
++// Create the noreturn report_size_overflow() function decl.
++static void size_overflow_start_unit(void __unused *gcc_data, void __unused *user_data)
++{
++ tree const_char_ptr_type_node;
++ tree fntype;
++
++ const_char_ptr_type_node = build_pointer_type(build_type_variant(char_type_node, 1, 0));
++
++ size_overflow_type_HI = create_typedef(intHI_type_node, "size_overflow_type_HI");
++ size_overflow_type_SI = create_typedef(intSI_type_node, "size_overflow_type_SI");
++ size_overflow_type_DI = create_typedef(intDI_type_node, "size_overflow_type_DI");
++ size_overflow_type_TI = create_typedef(intTI_type_node, "size_overflow_type_TI");
++
++ // void report_size_overflow(const char *loc_file, unsigned int loc_line, const char *current_func, const char *ssa_var)
++ fntype = build_function_type_list(void_type_node,
++ const_char_ptr_type_node,
++ unsigned_type_node,
++ const_char_ptr_type_node,
++ const_char_ptr_type_node,
++ NULL_TREE);
++ report_size_overflow_decl = build_fn_decl("report_size_overflow", fntype);
++
++ DECL_ASSEMBLER_NAME(report_size_overflow_decl);
++ TREE_PUBLIC(report_size_overflow_decl) = 1;
++ DECL_EXTERNAL(report_size_overflow_decl) = 1;
++ DECL_ARTIFICIAL(report_size_overflow_decl) = 1;
++ TREE_THIS_VOLATILE(report_size_overflow_decl) = 1;
++// !!!
++ DECL_PRESERVE_P(report_size_overflow_decl) = 1;
++ DECL_UNINLINABLE(report_size_overflow_decl) = 1;
++ TREE_USED(report_size_overflow_decl) = 1;
++ TREE_NOTHROW(report_size_overflow_decl) = 1;
++}
++
++
++int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
++{
++ int i;
++ const char * const plugin_name = plugin_info->base_name;
++ const int argc = plugin_info->argc;
++ const struct plugin_argument * const argv = plugin_info->argv;
++ bool enable = true;
++ struct register_pass_info insert_size_overflow_asm_pass_info;
++ struct register_pass_info size_overflow_functions_pass_info;
++
++ static const struct ggc_root_tab gt_ggc_r_gt_size_overflow[] = {
++ {
++ .base = &report_size_overflow_decl,
++ .nelt = 1,
++ .stride = sizeof(report_size_overflow_decl),
++ .cb = >_ggc_mx_tree_node,
++ .pchw = >_pch_nx_tree_node
++ },
++ LAST_GGC_ROOT_TAB
++ };
++
++ insert_size_overflow_asm_pass_info.pass = make_insert_size_overflow_asm_pass();
++ insert_size_overflow_asm_pass_info.reference_pass_name = "ssa";
++ insert_size_overflow_asm_pass_info.ref_pass_instance_number = 1;
++ insert_size_overflow_asm_pass_info.pos_op = PASS_POS_INSERT_AFTER;
++
++ size_overflow_functions_pass_info.pass = make_size_overflow_functions_pass();
++ size_overflow_functions_pass_info.reference_pass_name = "inline";
++ size_overflow_functions_pass_info.ref_pass_instance_number = 1;
++ size_overflow_functions_pass_info.pos_op = PASS_POS_INSERT_AFTER;
++
++ if (!plugin_default_version_check(version, &gcc_version)) {
++ error(G_("incompatible gcc/plugin versions"));
++ return 1;
++ }
++
++ for (i = 0; i < argc; ++i) {
++ if (!strcmp(argv[i].key, "no-size-overflow")) {
++ enable = false;
++ continue;
++ }
++ error(G_("unkown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
++ }
++
++ register_callback(plugin_name, PLUGIN_INFO, NULL, &size_overflow_plugin_info);
++ if (enable) {
++ register_callback(plugin_name, PLUGIN_START_UNIT, &size_overflow_start_unit, NULL);
++ register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS, NULL, (void *)>_ggc_r_gt_size_overflow);
++ register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &insert_size_overflow_asm_pass_info);
++ register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &size_overflow_functions_pass_info);
++ }
++ register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL);
++
++ return 0;
++}
+diff --git a/tools/gcc/size_overflow_plugin/size_overflow_plugin_hash.c b/tools/gcc/size_overflow_plugin/size_overflow_plugin_hash.c
+new file mode 100644
+index 0000000..2384252
+--- /dev/null
++++ b/tools/gcc/size_overflow_plugin/size_overflow_plugin_hash.c
+@@ -0,0 +1,345 @@
++/*
++ * Copyright 2011-2015 by Emese Revfy <re.emese@gmail.com>
++ * Licensed under the GPL v2, or (at your option) v3
++ *
++ * 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 "size_overflow_hash.h"
++#include "size_overflow_hash_aux.h"
++
++static const_tree get_function_type(const_tree decl)
++{
++ if (FUNCTION_PTR_P(decl))
++ return TREE_TYPE(TREE_TYPE(decl));
++ gcc_assert(TREE_CODE(decl) == FUNCTION_DECL);
++ return TREE_TYPE(decl);
++}
++
++static unsigned char get_tree_code(const_tree type)
++{
++ switch (TREE_CODE(type)) {
++ case ARRAY_TYPE:
++ return 0;
++ case BOOLEAN_TYPE:
++ return 1;
++ case ENUMERAL_TYPE:
++ return 2;
++ case FUNCTION_TYPE:
++ return 3;
++ case INTEGER_TYPE:
++ return 4;
++ case POINTER_TYPE:
++ return 5;
++ case RECORD_TYPE:
++ return 6;
++ case UNION_TYPE:
++ return 7;
++ case VOID_TYPE:
++ return 8;
++ case REAL_TYPE:
++ return 9;
++ case VECTOR_TYPE:
++ return 10;
++ case REFERENCE_TYPE:
++ return 11;
++ case OFFSET_TYPE:
++ return 12;
++ case COMPLEX_TYPE:
++ return 13;
++ default:
++ debug_tree((tree)type);
++ gcc_unreachable();
++ }
++}
++
++// http://www.team5150.com/~andrew/noncryptohashzoo2~/CrapWow.html
++static unsigned int CrapWow(const char *key, unsigned int len, unsigned int seed)
++{
++#define cwfold( a, b, lo, hi ) { p = (unsigned int)(a) * (unsigned long long)(b); lo ^= (unsigned int)p; hi ^= (unsigned int)(p >> 32); }
++#define cwmixa( in ) { cwfold( in, m, k, h ); }
++#define cwmixb( in ) { cwfold( in, n, h, k ); }
++
++ unsigned int m = 0x57559429;
++ unsigned int n = 0x5052acdb;
++ const unsigned int *key4 = (const unsigned int *)key;
++ unsigned int h = len;
++ unsigned int k = len + seed + n;
++ unsigned long long p;
++
++ while (len >= 8) {
++ cwmixb(key4[0]) cwmixa(key4[1]) key4 += 2;
++ len -= 8;
++ }
++ if (len >= 4) {
++ cwmixb(key4[0]) key4 += 1;
++ len -= 4;
++ }
++ if (len)
++ cwmixa(key4[0] & ((1 << (len * 8)) - 1 ));
++ cwmixb(h ^ (k + n));
++ return k ^ h;
++
++#undef cwfold
++#undef cwmixa
++#undef cwmixb
++}
++
++// For function pointer fields include the structure name in the hash
++static unsigned int get_type_name_hash(const_tree decl)
++{
++ const char *type_str;
++ unsigned int type_name_len;
++
++ if (!FUNCTION_PTR_P(decl))
++ return 0;
++ if (TREE_CODE(decl) == VAR_DECL)
++ return 0;
++
++ gcc_assert(TREE_CODE(decl) == FIELD_DECL);
++ type_str = get_type_name_from_field(decl);
++ if (!type_str)
++ return 0;
++ type_name_len = strlen(type_str);
++ return CrapWow(type_str, type_name_len, 0) & 0xffff;
++}
++
++static void set_hash(struct decl_hash *decl_hash_data)
++{
++ unsigned int fn, type, codes, seed = 0;
++
++ fn = CrapWow(decl_hash_data->fn_name, strlen(decl_hash_data->fn_name), seed) & 0xffff;
++ codes = CrapWow((const char*)decl_hash_data->tree_codes, decl_hash_data->tree_codes_len, seed) & 0xffff;
++ type = get_type_name_hash(decl_hash_data->decl);
++ decl_hash_data->hash = type ^ fn ^ codes;
++}
++
++static void set_decl_type_codes(const_tree type, struct decl_hash *decl_hash_data)
++{
++ gcc_assert(type != NULL_TREE);
++ gcc_assert(TREE_CODE_CLASS(TREE_CODE(type)) == tcc_type);
++
++ while (type && decl_hash_data->tree_codes_len < CODES_LIMIT) {
++ decl_hash_data->tree_codes[decl_hash_data->tree_codes_len] = get_tree_code(type);
++ decl_hash_data->tree_codes_len++;
++ type = TREE_TYPE(type);
++ }
++}
++
++static void set_result_codes(const_tree node, struct decl_hash *decl_hash_data)
++{
++ const_tree result;
++
++ gcc_assert(node != NULL_TREE);
++
++ if (DECL_P(node)) {
++ result = DECL_RESULT(node);
++ if (result != NULL_TREE)
++ return set_decl_type_codes(TREE_TYPE(result), decl_hash_data);
++ return set_result_codes(TREE_TYPE(node), decl_hash_data);
++ }
++
++ gcc_assert(TYPE_P(node));
++
++ if (TREE_CODE(node) == FUNCTION_TYPE)
++ return set_result_codes(TREE_TYPE(node), decl_hash_data);
++
++ return set_decl_type_codes(node, decl_hash_data);
++}
++
++static void set_decl_codes(struct decl_hash *decl_hash_data)
++{
++ const_tree arg, type;
++ enum tree_code code;
++
++ if (TREE_CODE(decl_hash_data->decl) == VAR_DECL) {
++ set_decl_type_codes(TREE_TYPE(decl_hash_data->decl), decl_hash_data);
++ return;
++ }
++
++ type = get_function_type(decl_hash_data->decl);
++ code = TREE_CODE(type);
++ gcc_assert(code == FUNCTION_TYPE || code == METHOD_TYPE);
++
++ if (FUNCTION_PTR_P(decl_hash_data->decl))
++ set_result_codes(type, decl_hash_data);
++ else
++ set_result_codes(decl_hash_data->decl, decl_hash_data);
++
++ for (arg = TYPE_ARG_TYPES(type); arg != NULL_TREE && decl_hash_data->tree_codes_len < CODES_LIMIT; arg = TREE_CHAIN(arg))
++ set_decl_type_codes(TREE_VALUE(arg), decl_hash_data);
++}
++
++static const struct size_overflow_hash *get_proper_hash_chain(const struct size_overflow_hash *entry, const char *func_name)
++{
++ while (entry) {
++ if (!strcmp(entry->name, func_name))
++ return entry;
++ entry = entry->next;
++ }
++ return NULL;
++}
++
++unsigned int get_decl_hash(const_tree decl, const char *decl_name)
++{
++ struct decl_hash decl_hash_data;
++ enum tree_code code = TREE_CODE(decl);
++
++ decl_hash_data.fn_name = decl_name;
++ gcc_assert(code == FIELD_DECL || code == FUNCTION_DECL || code == VAR_DECL);
++
++ // skip builtins __builtin_constant_p
++ if (code == FUNCTION_DECL && DECL_BUILT_IN(decl))
++ return NO_HASH;
++
++ decl_hash_data.decl = decl;
++ decl_hash_data.tree_codes_len = 0;
++
++ set_decl_codes(&decl_hash_data);
++ gcc_assert(decl_hash_data.tree_codes_len != 0);
++ set_hash(&decl_hash_data);
++ return decl_hash_data.hash;
++}
++
++const char *get_orig_decl_name(const_tree decl)
++{
++ const char *name;
++ unsigned int len;
++ const void *end;
++ const_tree orig_decl = DECL_ORIGIN(decl);
++
++ len = DECL_NAME_LENGTH(orig_decl);
++ name = DECL_NAME_POINTER(orig_decl);
++
++ /* Sometimes gcc loses the original cgraph node leaving only clones behind.
++ * In such cases we will extract the name from the clone and use it in the hash table
++ * without checking the parameter number on the original (unavailable) decl.
++ */
++
++ if (made_by_compiler(orig_decl)) {
++ end = memchr(name, '.', len);
++ gcc_assert(end);
++ len = (long)end - (long)name;
++ gcc_assert(len > 0);
++ }
++
++ return xstrndup(name, len);
++}
++
++const struct size_overflow_hash *get_size_overflow_hash_entry(unsigned int hash, const char *decl_name, unsigned int argnum)
++{
++ const struct size_overflow_hash *entry, *entry_node;
++
++ entry = size_overflow_hash[hash];
++ entry_node = get_proper_hash_chain(entry, decl_name);
++ if (entry_node && entry_node->param & (1U << argnum))
++ return entry_node;
++
++ entry = size_overflow_hash_aux[hash];
++ entry_node = get_proper_hash_chain(entry, decl_name);
++ if (entry_node && entry_node->param & (1U << argnum))
++ return entry_node;
++
++ return NULL;
++}
++
++const struct size_overflow_hash *get_size_overflow_hash_entry_tree(const_tree fndecl, unsigned int argnum)
++{
++ const struct size_overflow_hash *entry;
++ const_tree orig_decl;
++ unsigned int orig_argnum, hash;
++ const char *decl_name;
++
++ if (made_by_compiler(fndecl)) {
++ orig_decl = get_orig_fndecl(fndecl);
++ orig_argnum = get_correct_argnum(fndecl, orig_decl, argnum);
++ } else {
++ orig_decl = fndecl;
++ orig_argnum = argnum;
++ }
++
++ if (orig_argnum == CANNOT_FIND_ARG)
++ return NULL;
++
++ decl_name = get_orig_decl_name(orig_decl);
++ hash = get_decl_hash(orig_decl, decl_name);
++ if (hash == NO_HASH)
++ return NULL;
++
++ entry = get_size_overflow_hash_entry(hash, decl_name, orig_argnum);
++ return entry;
++}
++
++unsigned int find_arg_number_tree(const_tree arg, const_tree func)
++{
++ tree var;
++ unsigned int argnum = 1;
++
++ if (DECL_ARGUMENTS(func) == NULL_TREE)
++ return CANNOT_FIND_ARG;
++
++ if (TREE_CODE(arg) == SSA_NAME)
++ arg = SSA_NAME_VAR(arg);
++
++ for (var = DECL_ARGUMENTS(func); var; var = TREE_CHAIN(var), argnum++) {
++ if (!operand_equal_p(arg, var, 0) && strcmp(DECL_NAME_POINTER(var), DECL_NAME_POINTER(arg)))
++ continue;
++ if (!skip_types(var))
++ return argnum;
++ }
++
++ return CANNOT_FIND_ARG;
++}
++
++const_tree get_attribute(const char* attr_name, const_tree decl)
++{
++ const_tree attr = lookup_attribute(attr_name, DECL_ATTRIBUTES(decl));
++ if (attr && TREE_VALUE(attr))
++ return attr;
++ return NULL_TREE;
++}
++
++/* Check if the function has a size_overflow attribute or it is in the size_overflow hash table.
++ * If the function is missing everywhere then print the missing message into stderr.
++ */
++void print_missing_function(next_interesting_function_t node)
++{
++ unsigned int argnum, hash;
++ const struct size_overflow_hash *entry;
++ const char *decl_name;
++
++ if (node->marked == ASM_STMT_SO_MARK)
++ return;
++
++ if (node->orig_next_node) {
++ hash = node->orig_next_node->hash;
++ decl_name = node->orig_next_node->decl_name;
++ argnum = node->orig_next_node->num;
++ } else {
++ hash = node->hash;
++ decl_name = node->decl_name;
++ argnum = node->num;
++ }
++
++ entry = get_size_overflow_hash_entry(hash, decl_name, argnum);
++ if (entry)
++ return;
++
++ // inform() would be too slow
++ fprintf(stderr, "Function %s is missing from the size_overflow hash table +%s+%u+%u+\n", decl_name, decl_name, argnum, hash);
++}
++
+diff --git a/tools/gcc/size_overflow_plugin/size_overflow_transform.c b/tools/gcc/size_overflow_plugin/size_overflow_transform.c
+new file mode 100644
+index 0000000..b02158b