]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/cp/cp-ubsan.c
Update copyright years.
[thirdparty/gcc.git] / gcc / cp / cp-ubsan.c
index c0d1ffcfc43b614f92292e55f07b7a06ff2ff19e..2532521bfafc1ff7931f45b139b1c0c1a22bdde3 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
@@ -21,30 +21,11 @@ along with GCC; see the file COPYING3.  If not see
 #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.  */
 
@@ -54,9 +35,10 @@ cp_ubsan_instrument_vptr_p (tree type)
   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)
@@ -94,10 +76,15 @@ cp_ubsan_instrument_vptr (location_t loc, tree op, tree type, bool is_addr,
   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);
@@ -131,20 +118,38 @@ cp_ubsan_maybe_instrument_member_call (tree stmt)
 {
   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;
 }
 
@@ -218,7 +223,7 @@ cp_ubsan_check_member_access_r (tree *stmt_p, int *walk_subtrees, void *data)
       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;
@@ -267,17 +272,18 @@ cp_ubsan_instrument_member_accesses (tree *t_p)
 /* 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);
 }
@@ -290,3 +296,65 @@ cp_ubsan_maybe_instrument_cast_to_vbase (location_t loc, tree type, tree op)
   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;
+}