c++: ICE with -fsanitize=vptr and constexpr dynamic_cast [PR98103]

-fsanitize=vptr initializes all vtable pointers to null so that it can
catch invalid calls; see cp_ubsan_maybe_initialize_vtbl_ptrs.  That
means that evaluating a vtable reference can produce a null pointer
in this mode, so cxx_eval_dynamic_cast_fn should check that and give
and error.

gcc/cp/ChangeLog:

	PR c++/98103
	* constexpr.c (cxx_eval_dynamic_cast_fn): If the evaluating of vtable
	yields a null pointer, give an error and return.  Use objtype.

gcc/testsuite/ChangeLog:

	PR c++/98103
	* g++.dg/ubsan/vptr-18.C: New test.
This commit is contained in:
Marek Polacek 2020-12-02 14:33:13 -05:00
parent 5ea350d1d7
commit 0221c656bb
2 changed files with 35 additions and 1 deletions

View file

@ -1998,11 +1998,20 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
to the object under construction or destruction, this object is
considered to be a most derived object that has the type of the
constructor or destructor's class. */
tree vtable = build_vfield_ref (obj, TREE_TYPE (obj));
tree vtable = build_vfield_ref (obj, objtype);
vtable = cxx_eval_constant_expression (ctx, vtable, /*lval*/false,
non_constant_p, overflow_p);
if (*non_constant_p)
return call;
/* With -fsanitize=vptr, we initialize all vtable pointers to null,
so it's possible that we got a null pointer now. */
if (integer_zerop (vtable))
{
if (!ctx->quiet)
error_at (loc, "virtual table pointer is used uninitialized");
*non_constant_p = true;
return integer_zero_node;
}
/* VTABLE will be &_ZTV1A + 16 or similar, get _ZTV1A. */
vtable = extract_obj_from_addr_offset (vtable);
const tree mdtype = DECL_CONTEXT (vtable);

View file

@ -0,0 +1,25 @@
// PR c++/98103
// { dg-do compile { target c++20 } }
// { dg-additional-options "-fsanitize=vptr -fno-sanitize-recover=vptr" }
// Modified constexpr-dynamic17.C.
struct V {
virtual void f();
};
struct A : V { };
struct B : V {
constexpr B(V*, A*);
};
struct D : B, A {
constexpr D() : B((A*)this, this) { }
};
constexpr B::B(V* v, A* a)
{
dynamic_cast<B*>(a); // { dg-error "uninitialized" }
}
constexpr D d;