diff options
author | Marek Polacek <polacek@redhat.com> | 2014-10-10 17:19:25 +0000 |
---|---|---|
committer | Marek Polacek <mpolacek@gcc.gnu.org> | 2014-10-10 17:19:25 +0000 |
commit | 0e82f089719b464e4d5959b45555b3dd135ae745 (patch) | |
tree | eb4a7b7db8a68e97f6fffec5e090f0363f3e519d /gcc/ubsan.c | |
parent | c95e71bef8bec02c1edea04bd332503075472071 (diff) | |
download | gcc-0e82f089719b464e4d5959b45555b3dd135ae745.zip gcc-0e82f089719b464e4d5959b45555b3dd135ae745.tar.gz gcc-0e82f089719b464e4d5959b45555b3dd135ae745.tar.bz2 |
asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE.
* asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE.
* doc/invoke.texi: Document -fsanitize=object-size.
* flag-types.h (enum sanitize_code): Add SANITIZE_OBJECT_SIZE and
or it into SANITIZE_UNDEFINED.
* gimple-fold.c (gimple_fold_call): Optimize IFN_UBSAN_OBJECT_SIZE.
* internal-fn.c (expand_UBSAN_OBJECT_SIZE): New function.
* internal-fn.def (UBSAN_OBJECT_SIZE): Define.
* opts.c (common_handle_option): Handle -fsanitize=object-size.
* ubsan.c: Include tree-object-size.h.
(ubsan_type_descriptor): Call tree_to_uhwi instead of tree_to_shwi.
(ubsan_expand_bounds_ifn): Use false instead of 0.
(ubsan_expand_objsize_ifn): New function.
(instrument_object_size): New function.
(pass_ubsan::execute): Add object size instrumentation.
* ubsan.h (ubsan_expand_objsize_ifn): Declare.
testsuite/
* c-c++-common/ubsan/object-size-1.c: New test.
* c-c++-common/ubsan/object-size-2.c: New test.
* c-c++-common/ubsan/object-size-3.c: New test.
* c-c++-common/ubsan/object-size-4.c: New test.
* c-c++-common/ubsan/object-size-5.c: New test.
* c-c++-common/ubsan/object-size-6.c: New test.
* c-c++-common/ubsan/object-size-7.c: New test.
* c-c++-common/ubsan/object-size-8.c: New test.
* c-c++-common/ubsan/object-size-9.c: New test.
* g++.dg/ubsan/object-size-1.C: New test.
* gcc.dg/ubsan/object-size-9.c: New test.
From-SVN: r216099
Diffstat (limited to 'gcc/ubsan.c')
-rw-r--r-- | gcc/ubsan.c | 212 |
1 files changed, 207 insertions, 5 deletions
diff --git a/gcc/ubsan.c b/gcc/ubsan.c index c9a72ad..dde0418 100644 --- a/gcc/ubsan.c +++ b/gcc/ubsan.c @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see #include "realmpfr.h" #include "dfp.h" #include "builtins.h" +#include "tree-object-size.h" /* Map from a tree to a VAR_DECL tree. */ @@ -391,7 +392,7 @@ ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle) tree dom = TYPE_DOMAIN (t); if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST) pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC, - tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1); + tree_to_uhwi (TYPE_MAX_VALUE (dom)) + 1); else /* ??? We can't determine the variable name; print VLA unspec. */ pretty_name[pos++] = '*'; @@ -614,12 +615,12 @@ ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi) /* Create condition "if (index > bound)". */ basic_block then_bb, fallthru_bb; gimple_stmt_iterator cond_insert_point - = create_cond_insert_point (gsi, 0/*before_p*/, false, true, + = create_cond_insert_point (gsi, false, false, true, &then_bb, &fallthru_bb); index = fold_convert (TREE_TYPE (bound), index); index = force_gimple_operand_gsi (&cond_insert_point, index, - true/*simple_p*/, NULL_TREE, - false/*before*/, GSI_NEW_STMT); + true, NULL_TREE, + false, GSI_NEW_STMT); gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE); gimple_set_location (g, loc); gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT); @@ -830,6 +831,76 @@ ubsan_expand_null_ifn (gimple_stmt_iterator *gsip) return false; } +/* Expand UBSAN_OBJECT_SIZE internal call. */ + +bool +ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi) +{ + gimple stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); + gcc_assert (gimple_call_num_args (stmt) == 4); + + tree ptr = gimple_call_arg (stmt, 0); + tree offset = gimple_call_arg (stmt, 1); + tree size = gimple_call_arg (stmt, 2); + tree ckind = gimple_call_arg (stmt, 3); + gimple_stmt_iterator gsi_orig = *gsi; + gimple g; + + /* See if we can discard the check. */ + if (TREE_CODE (size) != INTEGER_CST + || integer_all_onesp (size)) + /* Yes, __builtin_object_size couldn't determine the + object size. */; + else + { + /* if (offset > objsize) */ + basic_block then_bb, fallthru_bb; + gimple_stmt_iterator cond_insert_point + = create_cond_insert_point (gsi, false, false, true, + &then_bb, &fallthru_bb); + g = gimple_build_cond (GT_EXPR, offset, size, NULL_TREE, NULL_TREE); + gimple_set_location (g, loc); + gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT); + + /* Generate __ubsan_handle_type_mismatch call. */ + *gsi = gsi_after_labels (then_bb); + if (flag_sanitize_undefined_trap_on_error) + g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + tree data + = ubsan_create_data ("__ubsan_objsz_data", 1, &loc, + ubsan_type_descriptor (TREE_TYPE (ptr), + UBSAN_PRINT_POINTER), + NULL_TREE, + build_zero_cst (pointer_sized_int_node), + ckind, + NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + enum built_in_function bcode + = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH + : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT; + tree p = make_ssa_name (pointer_sized_int_node, NULL); + g = gimple_build_assign_with_ops (NOP_EXPR, p, ptr, NULL_TREE); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + g = gimple_build_call (builtin_decl_explicit (bcode), 2, data, p); + } + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + + /* Point GSI to next logical statement. */ + *gsi = gsi_start_bb (fallthru_bb); + } + + /* Get rid of the UBSAN_OBJECT_SIZE call from the IR. */ + unlink_stmt_vdef (stmt); + gsi_remove (&gsi_orig, true); + return gsi_end_p (*gsi); +} + /* Instrument a memory reference. BASE is the base of MEM, IS_LHS says whether the pointer is on the left hand side of the assignment. */ @@ -1339,6 +1410,128 @@ instrument_nonnull_return (gimple_stmt_iterator *gsi) flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks; } +/* Instrument memory references. Here we check whether the pointer + points to an out-of-bounds location. */ + +static void +instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs) +{ + gimple stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); + tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt); + tree type; + HOST_WIDE_INT size_in_bytes; + + type = TREE_TYPE (t); + if (VOID_TYPE_P (type)) + return; + + switch (TREE_CODE (t)) + { + case COMPONENT_REF: + if (TREE_CODE (t) == COMPONENT_REF + && DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1)) != NULL_TREE) + { + tree repr = DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1)); + t = build3 (COMPONENT_REF, TREE_TYPE (repr), TREE_OPERAND (t, 0), + repr, NULL_TREE); + } + break; + case ARRAY_REF: + case INDIRECT_REF: + case MEM_REF: + case VAR_DECL: + case PARM_DECL: + case RESULT_DECL: + break; + default: + return; + } + + size_in_bytes = int_size_in_bytes (type); + if (size_in_bytes <= 0) + return; + + HOST_WIDE_INT bitsize, bitpos; + tree offset; + enum machine_mode mode; + int volatilep = 0, unsignedp = 0; + tree inner = get_inner_reference (t, &bitsize, &bitpos, &offset, &mode, + &unsignedp, &volatilep, false); + + if (bitpos % BITS_PER_UNIT != 0 + || bitsize != size_in_bytes * BITS_PER_UNIT) + return; + + bool decl_p = DECL_P (inner); + tree base = decl_p ? inner : TREE_OPERAND (inner, 0); + tree ptr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (t)), t); + + while (TREE_CODE (base) == SSA_NAME) + { + gimple def_stmt = SSA_NAME_DEF_STMT (base); + if (gimple_assign_ssa_name_copy_p (def_stmt) + || (gimple_assign_cast_p (def_stmt) + && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (def_stmt)))) + || (is_gimple_assign (def_stmt) + && gimple_assign_rhs_code (def_stmt) == POINTER_PLUS_EXPR)) + base = gimple_assign_rhs1 (def_stmt); + else + break; + } + + if (!POINTER_TYPE_P (TREE_TYPE (base)) && !DECL_P (base)) + return; + + tree sizet; + tree base_addr = base; + if (decl_p) + base_addr = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (base)), base); + unsigned HOST_WIDE_INT size = compute_builtin_object_size (base_addr, 0); + if (size != (unsigned HOST_WIDE_INT) -1) + sizet = build_int_cst (sizetype, size); + else if (optimize) + { + if (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION) + loc = input_location; + /* Generate __builtin_object_size call. */ + sizet = builtin_decl_explicit (BUILT_IN_OBJECT_SIZE); + sizet = build_call_expr_loc (loc, sizet, 2, base_addr, + integer_zero_node); + sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true, + GSI_SAME_STMT); + } + else + return; + + /* Generate UBSAN_OBJECT_SIZE (ptr, ptr+sizeof(*ptr)-base, objsize, ckind) + call. */ + /* ptr + sizeof (*ptr) - base */ + t = fold_build2 (MINUS_EXPR, sizetype, + fold_convert (pointer_sized_int_node, ptr), + fold_convert (pointer_sized_int_node, base_addr)); + t = fold_build2 (PLUS_EXPR, sizetype, t, TYPE_SIZE_UNIT (type)); + + /* Perhaps we can omit the check. */ + if (TREE_CODE (t) == INTEGER_CST + && TREE_CODE (sizet) == INTEGER_CST + && tree_int_cst_le (t, sizet)) + return; + + /* Nope. Emit the check. */ + t = force_gimple_operand_gsi (gsi, t, true, NULL_TREE, true, + GSI_SAME_STMT); + ptr = force_gimple_operand_gsi (gsi, ptr, true, NULL_TREE, true, + GSI_SAME_STMT); + tree ckind = build_int_cst (unsigned_char_type_node, + is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF); + gimple g = gimple_build_call_internal (IFN_UBSAN_OBJECT_SIZE, 4, + ptr, t, sizet, ckind); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); +} + namespace { const pass_data pass_data_ubsan = @@ -1368,7 +1561,8 @@ public: | SANITIZE_BOOL | SANITIZE_ENUM | SANITIZE_ALIGNMENT | SANITIZE_NONNULL_ATTRIBUTE - | SANITIZE_RETURNS_NONNULL_ATTRIBUTE) + | SANITIZE_RETURNS_NONNULL_ATTRIBUTE + | SANITIZE_OBJECT_SIZE) && current_function_decl != NULL_TREE && !lookup_attribute ("no_sanitize_undefined", DECL_ATTRIBUTES (current_function_decl)); @@ -1431,6 +1625,14 @@ pass_ubsan::execute (function *fun) bb = gimple_bb (stmt); } + if (flag_sanitize & SANITIZE_OBJECT_SIZE) + { + if (gimple_store_p (stmt)) + instrument_object_size (&gsi, true); + if (gimple_assign_load_p (stmt)) + instrument_object_size (&gsi, false); + } + gsi_next (&gsi); } } |