This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[C++ PATCH] Improve -fsanitize=vptr (PR c++/70035)
- From: Jakub Jelinek <jakub at redhat dot com>
- To: Jason Merrill <jason at redhat dot com>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: Fri, 4 Mar 2016 21:31:36 +0100
- Subject: [C++ PATCH] Improve -fsanitize=vptr (PR c++/70035)
- Authentication-results: sourceware.org; auth=none
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
Hi!
-fsanitize=vptr library side of instrumentation assumes that the vtable
pointers in objects are either NULL, or some vtable pointer, if it is random
garbage, it might crash.
The following patch attempts to clear the vtable pointers in objects next to
the spot where -flifetime-dse=2 clobbers the object.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2016-03-04 Jakub Jelinek <jakub@redhat.com>
PR c++/70035
* cp-tree.h (cp_ubsan_maybe_initialize_vtbl_ptrs): New prototype.
* decl.c (start_preparsed_function): Call
cp_ubsan_maybe_initialize_vtbl_ptrs if needed.
* cp-ubsan.c (cp_ubsan_dfs_initialize_vtbl_ptrs,
cp_ubsan_maybe_initialize_vtbl_ptrs): New functions.
* g++.dg/ubsan/pr70035.C: New test.
--- gcc/cp/cp-tree.h.jj 2016-02-26 08:57:10.000000000 +0100
+++ gcc/cp/cp-tree.h 2016-03-04 14:07:51.045471741 +0100
@@ -6940,6 +6940,7 @@ extern void cp_ubsan_maybe_instrument_me
extern void cp_ubsan_instrument_member_accesses (tree *);
extern tree cp_ubsan_maybe_instrument_downcast (location_t, tree, tree, tree);
extern tree cp_ubsan_maybe_instrument_cast_to_vbase (location_t, tree, tree);
+extern void cp_ubsan_maybe_initialize_vtbl_ptrs (tree);
/* -- end of C++ */
--- gcc/cp/decl.c.jj 2016-02-24 22:33:35.000000000 +0100
+++ gcc/cp/decl.c 2016-03-04 14:53:54.283672876 +0100
@@ -14136,6 +14136,13 @@ start_preparsed_function (tree decl1, tr
finish_expr_stmt (exprstmt);
}
+ if (!processing_template_decl
+ && DECL_CONSTRUCTOR_P (decl1)
+ && (flag_sanitize & SANITIZE_VPTR)
+ && !DECL_CLONED_FUNCTION_P (decl1)
+ && !implicit_default_ctor_p (decl1))
+ cp_ubsan_maybe_initialize_vtbl_ptrs (current_class_ptr);
+
return true;
}
--- gcc/cp/cp-ubsan.c.jj 2016-02-24 23:00:44.000000000 +0100
+++ gcc/cp/cp-ubsan.c 2016-03-04 14:07:24.107840416 +0100
@@ -272,3 +272,55 @@ cp_ubsan_maybe_instrument_cast_to_vbase
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) || BINFO_VIRTUAL_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_indirect_ref (base_ptr, RO_NULL,
+ tf_warning_or_error),
+ TREE_TYPE (binfo));
+ gcc_assert (vtbl_ptr != error_mark_node);
+
+ /* Assign NULL to the vptr. */
+ tree vtbl = build_zero_cst (TREE_TYPE (vtbl_ptr));
+ finish_expr_stmt (cp_build_modify_expr (vtbl_ptr, NOP_EXPR, vtbl,
+ tf_warning_or_error));
+ }
+
+ 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);
+
+ /* 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);
+}
--- gcc/testsuite/g++.dg/ubsan/pr70035.C.jj 2016-03-04 14:20:39.234960689 +0100
+++ gcc/testsuite/g++.dg/ubsan/pr70035.C 2016-03-04 14:55:36.988268869 +0100
@@ -0,0 +1,26 @@
+// PR c++/70035
+// { dg-do run }
+// { dg-shouldfail "ubsan" }
+// { dg-options "-fsanitize=vptr -fno-sanitize-recover=undefined" }
+
+struct A {
+ A (int) {}
+ virtual int foo () { return 1; }
+};
+struct B : public A {
+ using A::foo;
+ B (int x) : A (foo (x)) {}
+ int foo (int x) { return x * 2; }
+};
+
+int
+main ()
+{
+ B b (20);
+}
+
+// { dg-output "\[^\n\r]*pr70035.C:12:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'B'(\n|\r\n|\r)" }
+// { dg-output "0x\[0-9a-fA-F]*: note: object has invalid vptr(\n|\r\n|\r)" }
+// { dg-output " ?.. .. .. .. ?.. .. .. .. ?.. .. .. .. \[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output " ?\\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output " ?invalid vptr" }
Jakub