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

1172 lines
32 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"
static enum intentional_mark walk_use_def(gimple_set *visited, const_tree lhs);
static const char *get_asm_string(const gasm *stmt)
{
if (stmt)
return gimple_asm_string(stmt);
return NULL;
}
tree get_size_overflow_asm_input(const gasm *stmt)
{
gcc_assert(gimple_asm_ninputs(stmt) != 0);
return TREE_VALUE(gimple_asm_input_op(stmt, 0));
}
bool is_size_overflow_insert_check_asm(const gasm *stmt)
{
const char *str;
if (!is_size_overflow_asm(stmt))
return false;
str = get_asm_string(stmt);
if (!str)
return false;
return !strncmp(str, OK_ASM_STR, sizeof(OK_ASM_STR) - 1);
}
bool is_size_overflow_asm(const_gimple stmt)
{
const char *str;
if (!stmt)
return false;
if (gimple_code(stmt) != GIMPLE_ASM)
return false;
str = get_asm_string(as_a_const_gasm(stmt));
if (!str)
return false;
return !strncmp(str, SO_ASM_STR, sizeof(SO_ASM_STR) - 1);
}
static bool is_size_overflow_intentional_asm_turn_off(const gasm *stmt)
{
const char *str;
if (!is_size_overflow_asm(stmt))
return false;
str = get_asm_string(stmt);
if (!str)
return false;
return !strncmp(str, TURN_OFF_ASM_STR, sizeof(TURN_OFF_ASM_STR) - 1);
}
static bool is_size_overflow_intentional_asm_end(const gasm *stmt)
{
const char *str;
if (!is_size_overflow_asm(stmt))
return false;
str = get_asm_string(stmt);
if (!str)
return false;
return !strncmp(str, END_INTENTIONAL_ASM_STR, sizeof(END_INTENTIONAL_ASM_STR) - 1);
}
/* Get the param of the intentional_overflow attribute.
* * 0: MARK_END_INTENTIONAL
* * 1..MAX_PARAM: MARK_YES
* * -1: MARK_TURN_OFF
*/
static tree get_attribute_param(const_tree decl)
{
const_tree attr;
if (decl == NULL_TREE)
return NULL_TREE;
attr = get_attribute("intentional_overflow", decl);
if (attr)
return TREE_VALUE(attr);
return NULL_TREE;
}
// MARK_TURN_OFF
static bool is_turn_off_intentional_attr(const_tree decl)
{
const_tree param_head;
param_head = get_attribute_param(decl);
if (param_head == NULL_TREE)
return false;
if (tree_to_shwi(TREE_VALUE(param_head)) == -1)
return true;
return false;
}
// MARK_END_INTENTIONAL
static bool is_end_intentional_intentional_attr(const_tree decl)
{
const_tree param_head;
param_head = get_attribute_param(decl);
if (param_head == NULL_TREE)
return false;
if (tree_to_shwi(TREE_VALUE(param_head)) == 0)
return true;
return false;
}
// MARK_YES
static bool is_yes_intentional_attr(const_tree decl, unsigned int argnum)
{
tree param, param_head;
if (argnum == 0)
return false;
param_head = get_attribute_param(decl);
for (param = param_head; param; param = TREE_CHAIN(param)) {
int argval = tree_to_shwi(TREE_VALUE(param));
if (argval <= 0)
continue;
if (argnum == (unsigned int)argval)
return true;
}
return false;
}
static void print_missing_intentional(enum intentional_mark callee_attr, enum intentional_mark caller_attr, tree decl, unsigned int argnum)
{
const struct size_overflow_hash *hash;
struct fn_raw_data raw_data;
// location_t loc;
if (caller_attr == MARK_NO || caller_attr == MARK_END_INTENTIONAL || caller_attr == MARK_TURN_OFF)
return;
if (callee_attr == MARK_END_INTENTIONAL || callee_attr == MARK_YES)
return;
initialize_raw_data(&raw_data);
raw_data.decl = decl;
raw_data.num = argnum;
hash = get_size_overflow_hash_entry_tree(&raw_data, SIZE_OVERFLOW);
if (!hash)
return;
// !!!
// loc = DECL_SOURCE_LOCATION(decl);
// inform(loc, "The intentional_overflow attribute is missing from +%s+%u+", DECL_NAME_POINTER(decl), argnum);
}
// Get the field decl of a component ref for intentional_overflow checking
static const_tree search_field_decl(const_tree comp_ref)
{
const_tree field = NULL_TREE;
unsigned int i, len = TREE_OPERAND_LENGTH(comp_ref);
for (i = 0; i < len; i++) {
field = TREE_OPERAND(comp_ref, i);
if (TREE_CODE(field) == FIELD_DECL)
break;
}
gcc_assert(TREE_CODE(field) == FIELD_DECL);
return field;
}
/* Get the type of the intentional_overflow attribute of a node
* * MARK_TURN_OFF
* * MARK_YES
* * MARK_NO
* * MARK_END_INTENTIONAL
*/
enum intentional_mark get_intentional_attr_type(const_tree node)
{
const_tree cur_decl;
if (node == NULL_TREE)
return MARK_NO;
switch (TREE_CODE(node)) {
case COMPONENT_REF:
cur_decl = search_field_decl(node);
if (is_turn_off_intentional_attr(cur_decl))
return MARK_TURN_OFF;
if (is_end_intentional_intentional_attr(cur_decl))
return MARK_YES;
break;
case PARM_DECL: {
unsigned int argnum;
cur_decl = get_orig_fndecl(current_function_decl);
argnum = find_arg_number_tree(node, cur_decl);
if (argnum == CANNOT_FIND_ARG)
return MARK_NO;
if (is_yes_intentional_attr(cur_decl, argnum))
return MARK_YES;
if (is_end_intentional_intentional_attr(cur_decl))
return MARK_END_INTENTIONAL;
break;
}
case FUNCTION_DECL: {
const_tree fndecl = get_orig_fndecl(node);
if (is_turn_off_intentional_attr(fndecl))
return MARK_TURN_OFF;
if (is_end_intentional_intentional_attr(fndecl))
return MARK_END_INTENTIONAL;
break;
}
case FIELD_DECL:
case VAR_DECL:
if (is_end_intentional_intentional_attr(node))
return MARK_END_INTENTIONAL;
if (is_turn_off_intentional_attr(node))
return MARK_TURN_OFF;
default:
break;
}
return MARK_NO;
}
static enum intentional_mark walk_use_def_phi(gimple_set *visited, const_tree result)
{
enum intentional_mark mark = MARK_NO;
gphi *phi = as_a_gphi(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);
mark = walk_use_def(visited, arg);
if (mark != MARK_NO)
return mark;
}
return mark;
}
static enum intentional_mark walk_use_def_binary(gimple_set *visited, const_tree lhs)
{
enum intentional_mark mark;
tree rhs1, rhs2;
gassign *def_stmt = as_a_gassign(get_def_stmt(lhs));
rhs1 = gimple_assign_rhs1(def_stmt);
rhs2 = gimple_assign_rhs2(def_stmt);
mark = walk_use_def(visited, rhs1);
if (mark == MARK_NO)
return walk_use_def(visited, rhs2);
return mark;
}
enum intentional_mark get_so_asm_type(const_gimple stmt)
{
const gasm *asm_stmt;
if (!stmt)
return MARK_NO;
if (!is_size_overflow_asm(stmt))
return MARK_NO;
asm_stmt = as_a_const_gasm(stmt);
if (is_size_overflow_insert_check_asm(asm_stmt))
return MARK_NO;
if (is_size_overflow_intentional_asm_turn_off(asm_stmt))
return MARK_TURN_OFF;
if (is_size_overflow_intentional_asm_end(asm_stmt))
return MARK_END_INTENTIONAL;
return MARK_YES;
}
static enum intentional_mark walk_use_def(gimple_set *visited, const_tree lhs)
{
const_gimple def_stmt;
if (TREE_CODE(lhs) != SSA_NAME)
return get_intentional_attr_type(lhs);
def_stmt = get_def_stmt(lhs);
gcc_assert(def_stmt);
if (pointer_set_insert(visited, def_stmt))
return MARK_NO;
switch (gimple_code(def_stmt)) {
case GIMPLE_CALL:
case GIMPLE_RETURN:
return MARK_NO;
case GIMPLE_NOP:
return walk_use_def(visited, SSA_NAME_VAR(lhs));
case GIMPLE_ASM:
return get_so_asm_type(as_a_const_gasm(def_stmt));
case GIMPLE_PHI:
return walk_use_def_phi(visited, lhs);
case GIMPLE_ASSIGN:
switch (gimple_num_ops(def_stmt)) {
case 2:
return walk_use_def(visited, gimple_assign_rhs1(def_stmt));
case 3:
return walk_use_def_binary(visited, lhs);
}
default:
debug_gimple_stmt((gimple)def_stmt);
error("%s: unknown gimple code", __func__);
gcc_unreachable();
}
}
enum intentional_mark check_intentional_size_overflow_asm_and_attribute(const_tree var)
{
enum intentional_mark mark;
gimple_set *visited;
if (is_turn_off_intentional_attr(get_orig_fndecl(current_function_decl)))
return MARK_TURN_OFF;
visited = pointer_set_create();
mark = walk_use_def(visited, var);
pointer_set_destroy(visited);
return mark;
}
/* Search intentional_overflow attribute on caller and on callee too.
* -1 / MARK_TURN_OFF: means that overflow checking is turned off inside the function and
* parameters aren't tracked backwards.
* 1..31 / MARK_YES: e.g., 4 means that overflow checking is turned off on the fourth parameter inside
* the function.
* 0 / MARK_END_INTENTIONAL: means that overflow checking is turned off on all the parameters of the function
* in all callers (on a structure field means that overflow checking is turned off
* in all expressions involving the given field).
*/
enum intentional_mark check_intentional_attribute(const_gimple stmt, unsigned int argnum)
{
enum intentional_mark caller_mark, callee_mark;
tree fndecl;
const_tree orig_cur_fndecl, arg;
orig_cur_fndecl = get_orig_fndecl(current_function_decl);
// handle MARK_TURN_OFF early on the caller
if (is_turn_off_intentional_attr(orig_cur_fndecl))
return MARK_TURN_OFF;
// handle MARK_END_INTENTIONAL on the caller
if (is_end_intentional_intentional_attr(orig_cur_fndecl))
return MARK_END_INTENTIONAL;
switch (gimple_code(stmt)) {
case GIMPLE_RETURN:
gcc_assert(argnum == 0);
// for now ignore other intentional attribute types on returns
return MARK_NO;
case GIMPLE_CALL:
gcc_assert(argnum != 0);
gcc_assert(argnum <= gimple_call_num_args(stmt));
arg = gimple_call_arg(stmt, argnum - 1);
break;
default:
debug_gimple_stmt((gimple)stmt);
gcc_unreachable();
}
// XXX ideiglenesen 0-nal a fuggvenyen belul is ki van kapcsolva a dupolas, eddig igy mukodott a doksi ellenere
if (is_end_intentional_intentional_attr(orig_cur_fndecl))
return MARK_END_INTENTIONAL;
fndecl = get_interesting_orig_fndecl_from_stmt(as_a_const_gcall(stmt));
// handle MARK_TURN_OFF on the callee
if (is_turn_off_intentional_attr(fndecl))
return MARK_TURN_OFF;
// find a defining marked caller argument or struct field for arg
caller_mark = check_intentional_size_overflow_asm_and_attribute(arg);
// did we find a marked struct field?
if (caller_mark == MARK_TURN_OFF)
return MARK_TURN_OFF;
// handle MARK_END_INTENTIONAL on the callee
if (is_end_intentional_intentional_attr(fndecl))
callee_mark = MARK_END_INTENTIONAL;
// is it a marked callee parameter?
else if (is_yes_intentional_attr(fndecl, argnum))
callee_mark = MARK_YES;
else
callee_mark = MARK_NO;
// no intentional attribute found
if (callee_mark == MARK_NO && caller_mark == MARK_NO)
return MARK_NO;
// MARK_YES is meaningful only on the caller
if (caller_mark == MARK_NO && callee_mark == MARK_YES)
return MARK_NO;
// we found a code region where we don't want to duplicate
if (caller_mark == MARK_YES && callee_mark == MARK_END_INTENTIONAL)
return MARK_END_INTENTIONAL;
// ignore the intentional attribute on the callee if we didn't find a marked defining argument or struct field
if (caller_mark == MARK_NO && callee_mark == MARK_END_INTENTIONAL)
return MARK_NO;
// the callee is missing the intentional attribute (MARK_YES or MARK_END_INTENTIONAL)
if (caller_mark == MARK_YES) {
print_missing_intentional(callee_mark, caller_mark, fndecl, argnum);
return MARK_YES;
}
fprintf(stderr, "caller: %s callee: %s\n", DECL_NAME_POINTER(orig_cur_fndecl), DECL_NAME_POINTER(fndecl));
debug_gimple_stmt((gimple)stmt);
print_intentional_mark(caller_mark);
print_intentional_mark(callee_mark);
gcc_unreachable();
}
bool is_a_cast_and_const_overflow(const_tree no_const_rhs)
{
const_tree rhs1, lhs, rhs1_type, lhs_type;
enum machine_mode lhs_mode, rhs_mode;
gimple def_stmt = get_def_stmt(no_const_rhs);
if (!def_stmt || !gimple_assign_cast_p(def_stmt))
return false;
rhs1 = gimple_assign_rhs1(def_stmt);
lhs = gimple_assign_lhs(def_stmt);
rhs1_type = TREE_TYPE(rhs1);
lhs_type = TREE_TYPE(lhs);
rhs_mode = TYPE_MODE(rhs1_type);
lhs_mode = TYPE_MODE(lhs_type);
if (TYPE_UNSIGNED(lhs_type) == TYPE_UNSIGNED(rhs1_type) || lhs_mode != rhs_mode)
return false;
return true;
}
static unsigned int uses_num(tree node)
{
imm_use_iterator imm_iter;
use_operand_p use_p;
unsigned int num = 0;
FOR_EACH_IMM_USE_FAST(use_p, imm_iter, node) {
gimple use_stmt = USE_STMT(use_p);
if (use_stmt == NULL)
return num;
if (is_gimple_debug(use_stmt))
continue;
if (gimple_assign_cast_p(use_stmt) && is_size_overflow_type(gimple_assign_lhs(use_stmt)))
continue;
num++;
}
return num;
}
static bool no_uses(tree node)
{
return !uses_num(node);
}
// 3.8.5 mm/page-writeback.c __ilog2_u64(): ret, uint + uintmax; uint -> int; int max
bool is_const_plus_unsigned_signed_truncation(const_tree lhs)
{
tree rhs1, lhs_type, rhs_type, rhs2, not_const_rhs;
gimple def_stmt = get_def_stmt(lhs);
if (!def_stmt || !gimple_assign_cast_p(def_stmt))
return false;
rhs1 = gimple_assign_rhs1(def_stmt);
rhs_type = TREE_TYPE(rhs1);
lhs_type = TREE_TYPE(lhs);
if (TYPE_UNSIGNED(lhs_type) || !TYPE_UNSIGNED(rhs_type))
return false;
if (TYPE_MODE(lhs_type) != TYPE_MODE(rhs_type))
return false;
def_stmt = get_def_stmt(rhs1);
if (!def_stmt || !is_gimple_assign(def_stmt) || gimple_num_ops(def_stmt) != 3)
return false;
if (gimple_assign_rhs_code(def_stmt) != PLUS_EXPR)
return false;
rhs1 = gimple_assign_rhs1(def_stmt);
rhs2 = gimple_assign_rhs2(def_stmt);
if (!is_gimple_constant(rhs1) && !is_gimple_constant(rhs2))
return false;
if (is_gimple_constant(rhs2))
not_const_rhs = rhs1;
else
not_const_rhs = rhs2;
return no_uses(not_const_rhs);
}
static bool is_lt_signed_type_max(const_tree rhs)
{
const_tree new_type, type_max, type = TREE_TYPE(rhs);
if (!TYPE_UNSIGNED(type))
return true;
switch (TYPE_MODE(type)) {
case QImode:
new_type = intQI_type_node;
break;
case HImode:
new_type = intHI_type_node;
break;
case SImode:
new_type = intSI_type_node;
break;
case DImode:
new_type = intDI_type_node;
break;
default:
debug_tree(type);
gcc_unreachable();
}
type_max = TYPE_MAX_VALUE(new_type);
if (!tree_int_cst_lt(type_max, rhs))
return true;
return false;
}
static bool is_gt_zero(const_tree rhs)
{
const_tree type = TREE_TYPE(rhs);
if (TYPE_UNSIGNED(type))
return true;
if (!tree_int_cst_lt(rhs, integer_zero_node))
return true;
return false;
}
bool is_a_constant_overflow(const gassign *stmt, const_tree rhs)
{
if (gimple_assign_rhs_code(stmt) == MIN_EXPR)
return false;
if (!is_gimple_constant(rhs))
return false;
// if the const is between 0 and the max value of the signed type of the same bitsize then there is no intentional overflow
if (is_lt_signed_type_max(rhs) && is_gt_zero(rhs))
return false;
return true;
}
static tree change_assign_rhs(struct visited *visited, gassign *stmt, const_tree orig_rhs, tree new_rhs)
{
const_gimple assign;
gimple_stmt_iterator gsi = gsi_for_stmt(stmt);
tree origtype = TREE_TYPE(orig_rhs);
assign = build_cast_stmt(visited, origtype, new_rhs, CREATE_NEW_VAR, &gsi, BEFORE_STMT, false);
pointer_set_insert(visited->my_stmts, assign);
return get_lhs(assign);
}
tree handle_intentional_overflow(interesting_stmts_t expand_from, bool check_overflow, gassign *stmt, tree change_rhs, tree new_rhs2)
{
tree new_rhs, orig_rhs;
void (*gimple_assign_set_rhs)(gimple, tree);
tree rhs1 = gimple_assign_rhs1(stmt);
tree rhs2 = gimple_assign_rhs2(stmt);
tree lhs = gimple_assign_lhs(stmt);
if (!check_overflow)
return create_assign(expand_from->visited, stmt, lhs, AFTER_STMT);
if (change_rhs == NULL_TREE)
return create_assign(expand_from->visited, stmt, lhs, AFTER_STMT);
if (new_rhs2 == NULL_TREE) {
orig_rhs = rhs1;
gimple_assign_set_rhs = &gimple_assign_set_rhs1;
} else {
orig_rhs = rhs2;
gimple_assign_set_rhs = &gimple_assign_set_rhs2;
}
check_size_overflow(expand_from, stmt, TREE_TYPE(change_rhs), change_rhs, orig_rhs, BEFORE_STMT);
new_rhs = change_assign_rhs(expand_from->visited, stmt, orig_rhs, change_rhs);
gimple_assign_set_rhs(stmt, new_rhs);
update_stmt(stmt);
return create_assign(expand_from->visited, stmt, lhs, AFTER_STMT);
}
static bool is_subtraction_special(struct visited *visited, const gassign *stmt)
{
gimple rhs1_def_stmt, rhs2_def_stmt;
const_tree rhs1_def_stmt_rhs1, rhs2_def_stmt_rhs1, rhs1_def_stmt_lhs, rhs2_def_stmt_lhs;
enum machine_mode rhs1_def_stmt_rhs1_mode, rhs2_def_stmt_rhs1_mode, rhs1_def_stmt_lhs_mode, rhs2_def_stmt_lhs_mode;
const_tree rhs1 = gimple_assign_rhs1(stmt);
const_tree rhs2 = gimple_assign_rhs2(stmt);
if (is_gimple_constant(rhs1) || is_gimple_constant(rhs2))
return false;
if (gimple_assign_rhs_code(stmt) != MINUS_EXPR)
return false;
gcc_assert(TREE_CODE(rhs1) == SSA_NAME && TREE_CODE(rhs2) == SSA_NAME);
rhs1_def_stmt = get_def_stmt(rhs1);
rhs2_def_stmt = get_def_stmt(rhs2);
if (!gimple_assign_cast_p(rhs1_def_stmt) || !gimple_assign_cast_p(rhs2_def_stmt))
return false;
rhs1_def_stmt_rhs1 = gimple_assign_rhs1(rhs1_def_stmt);
rhs2_def_stmt_rhs1 = gimple_assign_rhs1(rhs2_def_stmt);
rhs1_def_stmt_lhs = gimple_assign_lhs(rhs1_def_stmt);
rhs2_def_stmt_lhs = gimple_assign_lhs(rhs2_def_stmt);
rhs1_def_stmt_rhs1_mode = TYPE_MODE(TREE_TYPE(rhs1_def_stmt_rhs1));
rhs2_def_stmt_rhs1_mode = TYPE_MODE(TREE_TYPE(rhs2_def_stmt_rhs1));
rhs1_def_stmt_lhs_mode = TYPE_MODE(TREE_TYPE(rhs1_def_stmt_lhs));
rhs2_def_stmt_lhs_mode = TYPE_MODE(TREE_TYPE(rhs2_def_stmt_lhs));
if (GET_MODE_BITSIZE(rhs1_def_stmt_rhs1_mode) <= GET_MODE_BITSIZE(rhs1_def_stmt_lhs_mode))
return false;
if (GET_MODE_BITSIZE(rhs2_def_stmt_rhs1_mode) <= GET_MODE_BITSIZE(rhs2_def_stmt_lhs_mode))
return false;
pointer_set_insert(visited->no_cast_check, rhs1_def_stmt);
pointer_set_insert(visited->no_cast_check, rhs2_def_stmt);
return true;
}
static gassign *create_binary_assign(struct visited *visited, enum tree_code code, gassign *stmt, tree rhs1, tree rhs2)
{
gassign *assign;
gimple_stmt_iterator gsi = gsi_for_stmt(stmt);
tree type = TREE_TYPE(rhs1);
tree lhs = create_new_var(type);
gcc_assert(types_compatible_p(type, TREE_TYPE(rhs2)));
assign = as_a_gassign(gimple_build_assign_with_ops(code, lhs, rhs1, rhs2));
gimple_assign_set_lhs(assign, make_ssa_name(lhs, assign));
gsi_insert_before(&gsi, assign, GSI_NEW_STMT);
update_stmt(assign);
pointer_set_insert(visited->my_stmts, assign);
return assign;
}
static tree cast_to_TI_type(struct visited *visited, gassign *stmt, tree node)
{
gimple_stmt_iterator gsi;
const_gimple cast_stmt;
tree type = TREE_TYPE(node);
if (types_compatible_p(type, intTI_type_node))
return node;
gsi = gsi_for_stmt(stmt);
cast_stmt = build_cast_stmt(visited, intTI_type_node, node, CREATE_NEW_VAR, &gsi, BEFORE_STMT, false);
pointer_set_insert(visited->my_stmts, cast_stmt);
return get_lhs(cast_stmt);
}
static tree get_def_stmt_rhs(struct visited *visited, const_tree var)
{
tree rhs1, def_stmt_rhs1;
gimple rhs1_def_stmt, def_stmt_rhs1_def_stmt, def_stmt;
def_stmt = get_def_stmt(var);
if (!gimple_assign_cast_p(def_stmt))
return NULL_TREE;
gcc_assert(gimple_code(def_stmt) != GIMPLE_NOP && pointer_set_contains(visited->my_stmts, def_stmt) && gimple_assign_cast_p(def_stmt));
rhs1 = gimple_assign_rhs1(def_stmt);
rhs1_def_stmt = get_def_stmt(rhs1);
if (!gimple_assign_cast_p(rhs1_def_stmt))
return rhs1;
def_stmt_rhs1 = gimple_assign_rhs1(rhs1_def_stmt);
def_stmt_rhs1_def_stmt = get_def_stmt(def_stmt_rhs1);
switch (gimple_code(def_stmt_rhs1_def_stmt)) {
case GIMPLE_CALL:
case GIMPLE_NOP:
case GIMPLE_ASM:
case GIMPLE_PHI:
return def_stmt_rhs1;
case GIMPLE_ASSIGN:
return rhs1;
default:
debug_gimple_stmt(def_stmt_rhs1_def_stmt);
gcc_unreachable();
}
}
tree handle_integer_truncation(interesting_stmts_t expand_from, const_tree lhs)
{
tree new_rhs1, new_rhs2;
tree new_rhs1_def_stmt_rhs1, new_rhs2_def_stmt_rhs1, new_lhs;
gassign *assign, *stmt = as_a_gassign(get_def_stmt(lhs));
tree rhs1 = gimple_assign_rhs1(stmt);
tree rhs2 = gimple_assign_rhs2(stmt);
if (!is_subtraction_special(expand_from->visited, stmt))
return NULL_TREE;
new_rhs1 = expand(expand_from, rhs1);
new_rhs2 = expand(expand_from, rhs2);
new_rhs1_def_stmt_rhs1 = get_def_stmt_rhs(expand_from->visited, new_rhs1);
new_rhs2_def_stmt_rhs1 = get_def_stmt_rhs(expand_from->visited, new_rhs2);
if (new_rhs1_def_stmt_rhs1 == NULL_TREE || new_rhs2_def_stmt_rhs1 == NULL_TREE)
return NULL_TREE;
if (!types_compatible_p(TREE_TYPE(new_rhs1_def_stmt_rhs1), TREE_TYPE(new_rhs2_def_stmt_rhs1))) {
new_rhs1_def_stmt_rhs1 = cast_to_TI_type(expand_from->visited, stmt, new_rhs1_def_stmt_rhs1);
new_rhs2_def_stmt_rhs1 = cast_to_TI_type(expand_from->visited, stmt, new_rhs2_def_stmt_rhs1);
}
assign = create_binary_assign(expand_from->visited, MINUS_EXPR, stmt, new_rhs1_def_stmt_rhs1, new_rhs2_def_stmt_rhs1);
new_lhs = gimple_assign_lhs(assign);
check_size_overflow(expand_from, assign, TREE_TYPE(new_lhs), new_lhs, rhs1, AFTER_STMT);
return dup_assign(expand_from->visited, stmt, lhs, new_rhs1, new_rhs2, NULL_TREE);
}
bool is_a_neg_overflow(const gassign *stmt, const_tree rhs)
{
const_gimple def_stmt;
if (TREE_CODE(rhs) != SSA_NAME)
return false;
if (gimple_assign_rhs_code(stmt) != PLUS_EXPR)
return false;
def_stmt = get_def_stmt(rhs);
if (!is_gimple_assign(def_stmt) || gimple_assign_rhs_code(def_stmt) != BIT_NOT_EXPR)
return false;
return true;
}
/* e.g., drivers/acpi/acpica/utids.c acpi_ut_execute_CID()
* ((count - 1) * sizeof(struct acpi_pnp_dee_id_list) -> (count + fffffff) * 16
* fffffff * 16 > signed max -> truncate
*/
static bool look_for_mult_and_add(const_gimple stmt)
{
const_tree res;
tree rhs1, rhs2, def_rhs1, def_rhs2, const_rhs, def_const_rhs;
const_gimple def_stmt;
if (!stmt || gimple_code(stmt) == GIMPLE_NOP)
return false;
if (!is_gimple_assign(stmt))
return false;
if (gimple_assign_rhs_code(stmt) != MULT_EXPR)
return false;
rhs1 = gimple_assign_rhs1(stmt);
rhs2 = gimple_assign_rhs2(stmt);
if (is_gimple_constant(rhs1)) {
const_rhs = rhs1;
def_stmt = get_def_stmt(rhs2);
} else if (is_gimple_constant(rhs2)) {
const_rhs = rhs2;
def_stmt = get_def_stmt(rhs1);
} else
return false;
if (!is_gimple_assign(def_stmt))
return false;
if (gimple_assign_rhs_code(def_stmt) != PLUS_EXPR && gimple_assign_rhs_code(def_stmt) != MINUS_EXPR)
return false;
def_rhs1 = gimple_assign_rhs1(def_stmt);
def_rhs2 = gimple_assign_rhs2(def_stmt);
if (is_gimple_constant(def_rhs1))
def_const_rhs = def_rhs1;
else if (is_gimple_constant(def_rhs2))
def_const_rhs = def_rhs2;
else
return false;
res = fold_binary_loc(gimple_location(def_stmt), MULT_EXPR, TREE_TYPE(const_rhs), const_rhs, def_const_rhs);
if (is_lt_signed_type_max(res) && is_gt_zero(res))
return false;
return true;
}
enum intentional_overflow_type add_mul_intentional_overflow(const gassign *stmt)
{
const_gimple def_stmt_1, def_stmt_2;
const_tree rhs1, rhs2;
bool add_mul_rhs1, add_mul_rhs2;
rhs1 = gimple_assign_rhs1(stmt);
def_stmt_1 = get_def_stmt(rhs1);
add_mul_rhs1 = look_for_mult_and_add(def_stmt_1);
rhs2 = gimple_assign_rhs2(stmt);
def_stmt_2 = get_def_stmt(rhs2);
add_mul_rhs2 = look_for_mult_and_add(def_stmt_2);
if (add_mul_rhs1)
return RHS1_INTENTIONAL_OVERFLOW;
if (add_mul_rhs2)
return RHS2_INTENTIONAL_OVERFLOW;
return NO_INTENTIONAL_OVERFLOW;
}
static gassign *get_dup_stmt(struct visited *visited, gassign *stmt)
{
gassign *my_stmt;
gimple_stmt_iterator gsi = gsi_for_stmt(stmt);
gsi_next(&gsi);
my_stmt = as_a_gassign(gsi_stmt(gsi));
gcc_assert(pointer_set_contains(visited->my_stmts, my_stmt));
if (gimple_assign_cast_p(stmt) && gimple_assign_cast_p(my_stmt))
return my_stmt;
if (gimple_assign_rhs_code(stmt) != gimple_assign_rhs_code(my_stmt)) {
fprintf(stderr, "%s != %s\n", get_tree_code_name(gimple_assign_rhs_code(stmt)), get_tree_code_name(gimple_assign_rhs_code(my_stmt)));
debug_gimple_stmt(stmt);
debug_gimple_stmt(my_stmt);
gcc_unreachable();
}
return my_stmt;
}
/* unsigned type -> unary or binary assign (rhs1 or rhs2 is constant)
* unsigned type cast to signed type, unsigned type: no more uses
* e.g., lib/vsprintf.c:simple_strtol()
* _10 = (unsigned long int) _9
* _11 = -_10;
* _12 = (long int) _11; (_11_ no more uses)
*/
static bool is_call_or_cast(gimple stmt)
{
return gimple_assign_cast_p(stmt) || is_gimple_call(stmt);
}
static bool is_unsigned_cast_or_call_def_stmt(const_tree node)
{
const_tree rhs;
gimple def_stmt;
if (node == NULL_TREE)
return true;
if (is_gimple_constant(node))
return true;
def_stmt = get_def_stmt(node);
if (!def_stmt)
return false;
if (is_call_or_cast(def_stmt))
return true;
if (!is_gimple_assign(def_stmt) || gimple_num_ops(def_stmt) != 2)
return false;
rhs = gimple_assign_rhs1(def_stmt);
def_stmt = get_def_stmt(rhs);
if (!def_stmt)
return false;
return is_call_or_cast(def_stmt);
}
void unsigned_signed_cast_intentional_overflow(struct visited *visited, gassign *stmt)
{
unsigned int use_num;
gassign *so_stmt;
const_gimple def_stmt;
const_tree rhs1, rhs2;
tree rhs = gimple_assign_rhs1(stmt);
tree lhs_type = TREE_TYPE(gimple_assign_lhs(stmt));
const_tree rhs_type = TREE_TYPE(rhs);
if (!(TYPE_UNSIGNED(rhs_type) && !TYPE_UNSIGNED(lhs_type)))
return;
if (GET_MODE_BITSIZE(TYPE_MODE(rhs_type)) != GET_MODE_BITSIZE(TYPE_MODE(lhs_type)))
return;
use_num = uses_num(rhs);
if (use_num != 1)
return;
def_stmt = get_def_stmt(rhs);
if (!def_stmt)
return;
if (!is_gimple_assign(def_stmt))
return;
rhs1 = gimple_assign_rhs1(def_stmt);
if (!is_unsigned_cast_or_call_def_stmt(rhs1))
return;
rhs2 = gimple_assign_rhs2(def_stmt);
if (!is_unsigned_cast_or_call_def_stmt(rhs2))
return;
if (gimple_num_ops(def_stmt) == 3 && !is_gimple_constant(rhs1) && !is_gimple_constant(rhs2))
return;
so_stmt = get_dup_stmt(visited, stmt);
create_up_and_down_cast(visited, so_stmt, lhs_type, gimple_assign_rhs1(so_stmt));
}
/* gcc intentional overflow
* e.g., skb_set_network_header(), skb_set_mac_header()
* -, int offset + u16 network_header
* offset = -x->props.header_len
* skb->network_header += offset;
*
* SSA
* _141 = -_140;
* _154 = (short unsigned int) _141;
* _155 = (size_overflow_type_SI) _154;
* _156 = _154 + _155; // 2x
* _157 = (short unsigned int) _156;
*/
static bool is_short_cast_neg(const_tree rhs)
{
const_tree cast_rhs;
const_gimple neg_stmt;
gimple neg_cast_stmt, cast_stmt = get_def_stmt(rhs);
if (!cast_stmt || !gimple_assign_cast_p(cast_stmt))
return false;
cast_rhs = gimple_assign_rhs1(cast_stmt);
if (GET_MODE_BITSIZE(TYPE_MODE(TREE_TYPE(cast_rhs))) >= GET_MODE_BITSIZE(TYPE_MODE(TREE_TYPE(rhs))))
return false;
neg_cast_stmt = get_def_stmt(cast_rhs);
if (!neg_cast_stmt || !gimple_assign_cast_p(neg_cast_stmt))
return false;
neg_stmt = get_def_stmt(gimple_assign_rhs1(neg_cast_stmt));
if (!neg_stmt || !is_gimple_assign(neg_stmt))
return false;
return gimple_assign_rhs_code(neg_stmt) == NEGATE_EXPR;
}
static bool check_add_stmt(const_tree node)
{
const_gimple add_stmt;
const_tree add_rhs1, add_rhs2;
if (node == NULL_TREE)
return false;
add_stmt = get_def_stmt(node);
if (!add_stmt || !is_gimple_assign(add_stmt) || gimple_assign_rhs_code(add_stmt) != PLUS_EXPR)
return false;
add_rhs1 = gimple_assign_rhs1(add_stmt);
add_rhs2 = gimple_assign_rhs2(add_stmt);
return is_short_cast_neg(add_rhs1) || is_short_cast_neg(add_rhs2);
}
bool neg_short_add_intentional_overflow(gassign *unary_stmt)
{
const_tree rhs1, add_rhs1, add_rhs2, cast_rhs;
gimple cast_stmt;
const_gimple add_stmt;
rhs1 = gimple_assign_rhs1(unary_stmt);
cast_stmt = get_def_stmt(rhs1);
if (!cast_stmt || !gimple_assign_cast_p(cast_stmt))
return false;
cast_rhs = gimple_assign_rhs1(cast_stmt);
if (GET_MODE_BITSIZE(TYPE_MODE(TREE_TYPE(cast_rhs))) <= GET_MODE_BITSIZE(TYPE_MODE(TREE_TYPE(rhs1))))
return false;
// one or two plus expressions
if (check_add_stmt(cast_rhs))
return true;
add_stmt = get_def_stmt(cast_rhs);
if (!add_stmt || !is_gimple_assign(add_stmt))
return false;
add_rhs1 = gimple_assign_rhs1(add_stmt);
if (check_add_stmt(add_rhs1))
return true;
add_rhs2 = gimple_assign_rhs2(add_stmt);
return check_add_stmt(add_rhs2);
}
/* True:
* _25 = (<unnamed-unsigned:1>) _24;
* r_5(D)->stereo = _25;
*/
bool is_bitfield_unnamed_cast(const_tree decl, gassign *assign)
{
const_tree rhs, type;
gimple def_stmt;
if (TREE_CODE(decl) != FIELD_DECL)
return false;
if (!DECL_BIT_FIELD_TYPE(decl))
return false;
if (gimple_num_ops(assign) != 2)
return false;
rhs = gimple_assign_rhs1(assign);
if (is_gimple_constant(rhs))
return false;
type = TREE_TYPE(rhs);
if (TREE_CODE(type) == BOOLEAN_TYPE)
return false;
def_stmt = get_def_stmt(rhs);
if (!gimple_assign_cast_p(def_stmt))
return false;
return TYPE_PRECISION(type) < CHAR_TYPE_SIZE;
}
static bool is_mult_const(const_tree lhs)
{
const_gimple def_stmt;
const_tree rhs1, rhs2;
def_stmt = get_def_stmt(lhs);
if (!def_stmt || !is_gimple_assign(def_stmt) || gimple_assign_rhs_code(def_stmt) != MULT_EXPR)
return false;
rhs1 = gimple_assign_rhs1(def_stmt);
rhs2 = gimple_assign_rhs2(def_stmt);
if (is_gimple_constant(rhs1))
return !is_lt_signed_type_max(rhs1);
else if (is_gimple_constant(rhs2))
return !is_lt_signed_type_max(rhs2);
return false;
}
/* True:
* fs/cifs/file.c cifs_write_from_iter()
* u32 = u64 - (u64 - constant) * constant
* wdata->tailsz = cur_len - (nr_pages - 1) * PAGE_SIZE;
*
* _51 = _50 * 4294963200;
* _52 = _49 + _51;
* _53 = _52 + 4096;
*/
bool uconst_neg_intentional_overflow(const gassign *stmt)
{
const_gimple def_stmt;
const_tree noconst_rhs;
tree rhs1, rhs2;
// _53 = _52 + const;
if (gimple_assign_rhs_code(stmt) != PLUS_EXPR)
return false;
rhs1 = gimple_assign_rhs1(stmt);
rhs2 = gimple_assign_rhs2(stmt);
if (is_gimple_constant(rhs1))
noconst_rhs = rhs2;
else if (is_gimple_constant(rhs2))
noconst_rhs = rhs1;
else
return false;
def_stmt = get_def_stmt(noconst_rhs);
// _52 = _49 + _51;
if (!def_stmt)
return false;
if (!is_gimple_assign(def_stmt))
return false;
if (gimple_assign_rhs_code(def_stmt) != PLUS_EXPR)
return false;
rhs1 = gimple_assign_rhs1(def_stmt);
rhs2 = gimple_assign_rhs2(def_stmt);
if (is_gimple_constant(rhs1) || is_gimple_constant(rhs2))
return false;
// _51 = _50 * gt signed type max;
return is_mult_const(rhs1) || is_mult_const(rhs2);
}
/* True:
* drivers/net/ethernet/via/via-velocity.c velocity_rx_refill()
* u16 = cpu_to_le16(s32) | const
* rd->size = cpu_to_le16(vptr->rx.buf_sz) | RX_INTEN;
*
* _36 = (signed short) _35;
* _37 = _36 | -32768;
* _38 = (short unsigned int) _37;
*/
bool short_or_neg_const_ushort(gassign *stmt)
{
const_tree rhs, lhs_type, rhs_type;
const_tree def_rhs1, def_rhs2;
const_gimple def_stmt;
gimple def_def_stmt = NULL;
if (!gimple_assign_cast_p(stmt))
return false;
// _38 = (short unsigned int) _37;
lhs_type = TREE_TYPE(gimple_assign_lhs(stmt));
if (!TYPE_UNSIGNED(lhs_type))
return false;
if (TYPE_MODE(lhs_type) != HImode)
return false;
rhs = gimple_assign_rhs1(stmt);
rhs_type = TREE_TYPE(rhs);
if (TYPE_UNSIGNED(rhs_type))
return false;
if (TYPE_MODE(rhs_type) != HImode)
return false;
// _37 = _36 | -32768;
def_stmt = get_def_stmt(rhs);
if (!def_stmt || !is_gimple_assign(def_stmt) || gimple_assign_rhs_code(def_stmt) != BIT_IOR_EXPR)
return false;
def_rhs1 = gimple_assign_rhs1(def_stmt);
def_rhs2 = gimple_assign_rhs2(def_stmt);
if (is_gimple_constant(def_rhs1) && !is_gt_zero(def_rhs1))
def_def_stmt = get_def_stmt(def_rhs2);
else if (is_gimple_constant(def_rhs2) && !is_gt_zero(def_rhs2))
def_def_stmt = get_def_stmt(def_rhs1);
// _36 = (signed short) _35;
return def_def_stmt && gimple_assign_cast_p(def_def_stmt);
}