Bug 63956 - [5 Regression][UBSAN] ICE segfault in cxx_eval_call_expression ../../gcc/cp/constexpr.c
Summary: [5 Regression][UBSAN] ICE segfault in cxx_eval_call_expression ../../gcc/cp/c...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: sanitizer (show other bugs)
Version: 5.0
: P3 normal
Target Milestone: 5.0
Assignee: Marek Polacek
URL:
Keywords: ice-on-valid-code
Depends on:
Blocks:
 
Reported: 2014-11-19 11:19 UTC by Tobias Burnus
Modified: 2014-12-01 15:31 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2014-11-19 00:00:00


Attachments
Test case (scan16.ii) (656 bytes, text/plain)
2014-11-19 11:19 UTC, Tobias Burnus
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Tobias Burnus 2014-11-19 11:19:33 UTC
Created attachment 34035 [details]
Test case (scan16.ii)

That's a new regression. It was working when I reduce PR63813 (i.e. on 2014-11-11) as that ICE came later. It now ICEs with todays GCC (r217759).

g++ -S -std=c++11 -fsanitize=undefined scan16.ii

scan16.ii:51:28:   in constexpr expansion of ‘std::validator.std::unique_ptr<std::Validator>::unique_ptr()’
scan16.ii:51:28: internal compiler error: Segmentation fault
   unique_ptr < Validator > validator;
                            ^
0xcba72f crash_signal
        ../../gcc/toplev.c:359
0x81ea9a cxx_eval_call_expression
        ../../gcc/cp/constexpr.c:1147
0x820b1c cxx_eval_constant_expression
        ../../gcc/cp/constexpr.c:2864
Comment 1 Marek Polacek 2014-11-19 11:27:08 UTC
Confirmed.
Comment 2 Marek Polacek 2014-11-19 11:33:59 UTC
It looks like cxx_eval_call_expression just isn't prepared for internal functions, with NULL_TREE CALL_EXPR_FN.
Comment 3 Jakub Jelinek 2014-11-19 12:15:22 UTC
Supposedly with the C++14 constexpr changes we need to be prepared to handle all the ubsan builtins and internal calls we create during genericization.
Already in the still pending -fsanitize=vptr patch I had to deal with handling the ubsan vptr type cache miss builtin even before the C++14 constexpr changes, because that is created during static_cast handling, but if we now look at DECL_SAVED_TREE, it can see all the builtins / internal calls in there.
I hope most of the ubsan stuff can be ignored for constexpr purposes.
Comment 4 Jason Merrill 2014-11-19 22:09:16 UTC
(In reply to Jakub Jelinek from comment #3)
> Supposedly with the C++14 constexpr changes we need to be prepared to handle
> all the ubsan builtins and internal calls we create during genericization.

Hmm, maybe I should make a copy of the pre-genericization DECL_SAVED_TREE instead...
Comment 5 Jakub Jelinek 2014-11-19 22:12:22 UTC
If it would be just because of these 3-4 builtins/internal calls, then it is not worth it IMHO, making a copy would increase compiler memory footprint.
Already from the earlier phases there are some ubsan builtins in there anyway (shift instrumentation e.g.).
Comment 6 Marek Polacek 2014-11-19 22:36:58 UTC
Earlier today I tried the following and it seemed to work, but I don't know constexpr.c, so it may be totally bogus.

--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -1149,6 +1149,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
   constexpr_call *entry;
   bool depth_ok;
 
+  if (fun == NULL_TREE
+      && CALL_EXPR_IFN (t) == IFN_UBSAN_NULL)
+    return t;
+
   if (TREE_CODE (fun) != FUNCTION_DECL)
     {
       /* Might be a constexpr function pointer.  */
Comment 7 Jakub Jelinek 2014-11-19 22:42:42 UTC
(In reply to Marek Polacek from comment #6)
> Earlier today I tried the following and it seemed to work, but I don't know
> constexpr.c, so it may be totally bogus.
> 
> --- gcc/cp/constexpr.c
> +++ gcc/cp/constexpr.c
> @@ -1149,6 +1149,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx,
> tree t,
>    constexpr_call *entry;
>    bool depth_ok;
>  
> +  if (fun == NULL_TREE
> +      && CALL_EXPR_IFN (t) == IFN_UBSAN_NULL)
> +    return t;
> +
>    if (TREE_CODE (fun) != FUNCTION_DECL)
>      {
>        /* Might be a constexpr function pointer.  */

The -fsanitize=vptr patch has:
   if (is_builtin_fn (fun))
-    return cxx_eval_builtin_function_call (old_call, t, allow_non_constant,
-                                          addr, non_constant_p, overflow_p);
+    {
+      /* Ignore -fsanitize=vptr instrumentation.  */
+      if ((flag_sanitize & SANITIZE_VPTR)
+         && DECL_BUILT_IN_CLASS (fun) == BUILT_IN_NORMAL
+         && (DECL_FUNCTION_CODE (fun)
+             == ((flag_sanitize_recover & SANITIZE_VPTR)
+                 ? BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS
+                 : BUILT_IN_UBSAN_HANDLE_DYNAMIC_TYPE_CACHE_MISS_ABORT))
+         && call_expr_nargs (t) == 4)
+       return void_node;
+
+      return cxx_eval_builtin_function_call (old_call, t, allow_non_constant,
+                                            addr, non_constant_p, overflow_p);
+    }

hunk in it to ignore __builtin___ubsan_handle_dynamic_type_cache_miss*.
For fun == NULL_TREE, guess what you want is a switch on the CALL_EXPR_IFN value and enumerate there all the internal calls and elsewhere builtins that should be ignored.
Comment 8 Marek Polacek 2014-11-19 22:54:37 UTC
Thanks, will try that tomorrow.

So to ignore the call I should return void_node instead of t, good to know.
Comment 9 Jakub Jelinek 2014-11-20 07:18:11 UTC
In the light of -std=c++14 constexprs, please try to write testcases like (-std=c++14 -fsanitize=undefined,float-divide-by-zero,float-cast-overflow):

constexpr int
f1 (int a, int b)
{
  if (b != 2)
    a <<= b;
  return a;
}

constexpr int x1 = f1 (5, 3);
constexpr int x2 = f1 (5, -2);

constexpr int
f2 (int a, int b)
{
  if (b != 2)
    a = a / b;
  return a;
}

constexpr int x3 = f2 (5, 3);
constexpr int x4 = f2 (7, 0);
constexpr int x5 = f2 (-__INT_MAX__ - 1, -1);

constexpr float
f3 (float a, float b)
{
  if (b != 2.0)
    a = a / b;
  return a;
}

constexpr float x6 = f3 (5.0, 3.0);
constexpr float x7 = f3 (7.0, 0.0);

constexpr int
f4 (const int *a, int b)
{
  if (b != 2)
    b = a[b];
  return b;
}

constexpr int x8[4] = { 1, 2, 3, 4 };
constexpr int x9 = f4 (x8, 3);
constexpr int x10 = f4 (x8, 4);

constexpr int
f5 (const int &a, int b)
{
  if (b != 2)
    b = a;
  return b;
}

constexpr int
f6 (const int *a, int b)
{
  if (b != 3)
    return f5 (*a, b);
  return 7;
}

constexpr int x12 = 7;
constexpr int x13 = f6 (&x12, 5);
constexpr int x14 = f6 ((const int *) 0, 8);

(and add for all the other stuff we ubsan instrument in the FEs).
For the first snippet we e.g. emit:
m1.C:10:23:   in constexpr expansion of ‘f1(5, -2)’
m1.C:5:7: error: ‘<ubsan routine call>’ is not a constant expression
     a <<= b;
       ^
m1.C:10:29: error: constexpr call flows off the end of the function
 constexpr int x2 = f1 (5, -2);
                             ^
I'd say we should not, we should just ignore the ubsan routine call.
If C++14 constexprs are supposed to be invalid if there is undefined behavior in them while being interpreted by the compiler with the given arguments, then
supposedly the FE should regardless of -fsanitize=undefined error out or warn and say exactly what is invalid in there, talking about <ubsan routine call>
is just too confusing.  Don't know if rejecting it is just QoI or a requirement in C++14.
And on the last snippet we ICE, that is the internal call.
Haven't added all the cases there though, and even e.g. for shift I haven't tried to call it with all the kinds of arguments that are invalid in C++14.
Comment 10 Marek Polacek 2014-11-20 16:12:54 UTC
BTW, wouldn't it make sense to not instrument things in constexpr functions?  It is not always possible, but at least in cp/ we could skip instrumentation if DECL_DECLARED_CONSTEXPR_P (current_function_decl), similarly as we check for no_sanitize_undefined.
Comment 11 Jakub Jelinek 2014-11-20 16:15:08 UTC
I thought you can call constexpr functions outside of constexpr contexts with non-constant arguments and they are executed then as any other function, so not instrumenting would mean you'd miss undefined behavior diagnostics.
Comment 12 Marek Polacek 2014-11-20 16:22:31 UTC
Of course.  Thanks.
Comment 13 Marek Polacek 2014-12-01 15:29:43 UTC
Author: mpolacek
Date: Mon Dec  1 15:29:11 2014
New Revision: 218221

URL: https://gcc.gnu.org/viewcvs?rev=218221&root=gcc&view=rev
Log:
	PR sanitizer/63956
	* ubsan.c (is_ubsan_builtin_p): Check also built-in class.
cp/
	* constexpr.c: Include ubsan.h.
	(cxx_eval_call_expression): Bail out for IFN_UBSAN_{NULL,BOUNDS}
	internal functions and for ubsan builtins.
	* error.c: Include internal-fn.h.
	(dump_expr): Add printing of internal functions.
testsuite/
	* c-c++-common/ubsan/shift-5.c: Add xfails.
	* g++.dg/ubsan/div-by-zero-1.C: Don't use -w.  Add xfail.
	* g++.dg/ubsan/pr63956.C: New test.

Added:
    trunk/gcc/testsuite/g++.dg/ubsan/pr63956.C
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/constexpr.c
    trunk/gcc/cp/error.c
    trunk/gcc/testsuite/ChangeLog
    trunk/gcc/testsuite/c-c++-common/ubsan/shift-5.c
    trunk/gcc/testsuite/g++.dg/ubsan/div-by-zero-1.C
    trunk/gcc/ubsan.c
Comment 14 Marek Polacek 2014-12-01 15:31:07 UTC
Fixed.