/* UndefinedBehaviorSanitizer, undefined behavior detector.
- Copyright (C) 2014 Free Software Foundation, Inc.
+ Copyright (C) 2014-2022 Free Software Foundation, Inc.
Contributed by Jakub Jelinek <jakub@redhat.com>
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "alias.h"
-#include "symtab.h"
-#include "options.h"
-#include "tree.h"
-#include "alloc-pool.h"
-#include "output.h"
-#include "toplev.h"
-#include "ubsan.h"
#include "cp-tree.h"
-#include "c-family/c-common.h"
-#include "c-family/c-ubsan.h"
-#include "asan.h"
-#include "internal-fn.h"
-#include "stor-layout.h"
-#include "builtins.h"
-#include "fold-const.h"
+#include "ubsan.h"
#include "stringpool.h"
-#include "predict.h"
-#include "tree-ssa-alias.h"
-#include "basic-block.h"
-#include "gimple-expr.h"
-#include "gimple.h"
-#include "lto-streamer.h"
-#include "cgraph.h"
+#include "attribs.h"
+#include "asan.h"
/* Test if we should instrument vptr access. */
if (!flag_rtti || flag_sanitize_undefined_trap_on_error)
return false;
- if (current_function_decl
- && lookup_attribute ("no_sanitize_undefined",
- DECL_ATTRIBUTES (current_function_decl)))
+ if (!sanitize_flags_p (SANITIZE_VPTR))
+ return false;
+
+ if (current_function_decl == NULL_TREE)
return false;
if (type)
vptr = fold_convert_loc (loc, pointer_sized_int_node, vptr);
vptr = fold_convert_loc (loc, uint64_type_node, vptr);
if (ckind == UBSAN_DOWNCAST_POINTER)
- vptr = fold_build3 (COND_EXPR, uint64_type_node,
- fold_build2 (NE_EXPR, boolean_type_node, op,
- build_zero_cst (TREE_TYPE (op))),
- vptr, build_int_cst (uint64_type_node, 0));
+ {
+ tree cond = build2_loc (loc, NE_EXPR, boolean_type_node, op,
+ build_zero_cst (TREE_TYPE (op)));
+ /* This is a compiler generated comparison, don't emit
+ e.g. -Wnonnull-compare warning for it. */
+ suppress_warning (cond, OPT_Wnonnull_compare);
+ vptr = build3_loc (loc, COND_EXPR, uint64_type_node, cond,
+ vptr, build_int_cst (uint64_type_node, 0));
+ }
tree ti_decl = get_tinfo_decl (type);
mark_used (ti_decl);
tree ptype = build_pointer_type (type);
{
if (call_expr_nargs (stmt) == 0)
return;
- tree *opp = &CALL_EXPR_ARG (stmt, 0);
- tree op = *opp;
- if (op == error_mark_node
- || !POINTER_TYPE_P (TREE_TYPE (op)))
- return;
- while (TREE_CODE (op) == COMPOUND_EXPR)
+ tree op, *opp;
+
+ tree fn = CALL_EXPR_FN (stmt);
+ if (fn && TREE_CODE (fn) == OBJ_TYPE_REF)
+ {
+ /* Virtual function call: Sanitize the use of the object pointer in the
+ OBJ_TYPE_REF, since the vtable reference will SEGV otherwise (95221).
+ OBJ_TYPE_REF_EXPR is ptr->vptr[N] and OBJ_TYPE_REF_OBJECT is ptr. But
+ we can't be sure of finding OBJ_TYPE_REF_OBJECT in OBJ_TYPE_REF_EXPR
+ if the latter has been optimized, so we use a COMPOUND_EXPR below. */
+ opp = &OBJ_TYPE_REF_EXPR (fn);
+ op = OBJ_TYPE_REF_OBJECT (fn);
+ }
+ else
{
- opp = &TREE_OPERAND (op, 1);
+ /* Non-virtual call: Sanitize the 'this' argument. */
+ opp = &CALL_EXPR_ARG (stmt, 0);
+ if (*opp == error_mark_node
+ || !INDIRECT_TYPE_P (TREE_TYPE (*opp)))
+ return;
+ while (TREE_CODE (*opp) == COMPOUND_EXPR)
+ opp = &TREE_OPERAND (*opp, 1);
op = *opp;
}
op = cp_ubsan_maybe_instrument_vptr (EXPR_LOCATION (stmt), op,
TREE_TYPE (TREE_TYPE (op)),
true, UBSAN_MEMBER_CALL);
- if (op)
+ if (!op)
+ /* No change. */;
+ else if (fn && TREE_CODE (fn) == OBJ_TYPE_REF)
+ *opp = cp_build_compound_expr (op, *opp, tf_none);
+ else
*opp = op;
}
if (TREE_CODE (t) == ADDR_EXPR)
{
*walk_subtrees = 0;
- t = TREE_OPERAND (stmt, 0);
+ t = TREE_OPERAND (t, 0);
cp_walk_tree (&t, cp_ubsan_check_member_access_r, data, ucmd->pset);
}
break;
/* Instrument downcast. */
tree
-cp_ubsan_maybe_instrument_downcast (location_t loc, tree type, tree op)
+cp_ubsan_maybe_instrument_downcast (location_t loc, tree type,
+ tree intype, tree op)
{
- if (!POINTER_TYPE_P (type)
- || !POINTER_TYPE_P (TREE_TYPE (op))
- || !CLASS_TYPE_P (TREE_TYPE (type))
+ if (!INDIRECT_TYPE_P (type)
+ || !INDIRECT_TYPE_P (intype)
+ || !INDIRECT_TYPE_P (TREE_TYPE (op))
|| !CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (op)))
- || !DERIVED_FROM_P (TREE_TYPE (TREE_TYPE (op)), TREE_TYPE (type)))
+ || !is_properly_derived_from (TREE_TYPE (type), TREE_TYPE (intype)))
return NULL_TREE;
return cp_ubsan_maybe_instrument_vptr (loc, op, TREE_TYPE (type), true,
- TREE_CODE (type) == POINTER_TYPE
+ TYPE_PTR_P (type)
? UBSAN_DOWNCAST_POINTER
: UBSAN_DOWNCAST_REFERENCE);
}
return cp_ubsan_maybe_instrument_vptr (loc, op, type, true,
UBSAN_CAST_TO_VBASE);
}
+
+/* Called from initialize_vtbl_ptrs via dfs_walk. BINFO is the base
+ which we want to initialize the vtable pointer for, DATA is
+ TREE_LIST whose TREE_VALUE is the this ptr expression. */
+
+static tree
+cp_ubsan_dfs_initialize_vtbl_ptrs (tree binfo, void *data)
+{
+ if (!TYPE_CONTAINS_VPTR_P (BINFO_TYPE (binfo)))
+ return dfs_skip_bases;
+
+ if (!BINFO_PRIMARY_P (binfo))
+ {
+ tree base_ptr = TREE_VALUE ((tree) data);
+
+ base_ptr = build_base_path (PLUS_EXPR, base_ptr, binfo, /*nonnull=*/1,
+ tf_warning_or_error);
+
+ /* Compute the location of the vtpr. */
+ tree vtbl_ptr
+ = build_vfield_ref (cp_build_fold_indirect_ref (base_ptr),
+ TREE_TYPE (binfo));
+ gcc_assert (vtbl_ptr != error_mark_node);
+
+ /* Assign NULL to the vptr. */
+ tree vtbl = build_zero_cst (TREE_TYPE (vtbl_ptr));
+ tree stmt = cp_build_modify_expr (input_location, vtbl_ptr, NOP_EXPR,
+ vtbl, tf_warning_or_error);
+ if (vptr_via_virtual_p (binfo))
+ /* If this vptr comes from a virtual base of the complete object, only
+ clear it if we're in charge of virtual bases. */
+ stmt = build_if_in_charge (stmt);
+ finish_expr_stmt (stmt);
+ }
+
+ return NULL_TREE;
+}
+
+/* Initialize all the vtable pointers in the object pointed to by
+ ADDR to NULL, so that we catch invalid calls to methods before
+ mem-initializers are completed. */
+
+void
+cp_ubsan_maybe_initialize_vtbl_ptrs (tree addr)
+{
+ if (!cp_ubsan_instrument_vptr_p (NULL_TREE))
+ return;
+
+ tree type = TREE_TYPE (TREE_TYPE (addr));
+ tree list = build_tree_list (type, addr);
+ /* We cannot rely on the vtable being set up. We have to indirect via the
+ vtt_parm. */
+ int save_in_base_initializer = in_base_initializer;
+ in_base_initializer = 1;
+
+ /* Walk through the hierarchy, initializing the vptr in each base
+ class to NULL. */
+ dfs_walk_once (TYPE_BINFO (type), cp_ubsan_dfs_initialize_vtbl_ptrs,
+ NULL, list);
+
+ in_base_initializer = save_in_base_initializer;
+}