Bug 83182 - undefined behavior in generic_wide_int due to missing input validation
Summary: undefined behavior in generic_wide_int due to missing input validation
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: other (show other bugs)
Version: 8.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: ice-checking, internal-improvement
Depends on:
Blocks:
 
Reported: 2017-11-27 17:15 UTC by Martin Sebor
Modified: 2021-08-24 09:06 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Sebor 2017-11-27 17:15:43 UTC
A number of members of the wide_int classes use the expression (LEN - 1) as an index into a small member array.  For example:

  template <typename storage>
  inline HOST_WIDE_INT
  generic_wide_int <storage>::sign_mask () const
  {
    unsigned int len = this->get_len ();
    unsigned HOST_WIDE_INT high = this->get_val ()[len - 1];
    ...
  }

However, the wide_int constructors do not sufficiently validate their preconditions.  As a result, when the get_len () function resturns zero the behavior of the functions that use the decremented result as an index is undefined.

The problem can be triggered by the following test case.  When the use of the invalid offset_int is far removed from its construction it can be difficult to debug.  To make debugging easier, the constructor and/or the wi::to_offset function should validate their input when checking is enabled.  Functions that use the result of (LEN - 1) as an index should also assert that the result is valid.

  {
    // Create an expression that doesn't evaluate to INTEGER_CST.
    tree x = build2 (TRUNC_DIV_EXPR, integer_type_node, integer_one_node, integer_zero_node);

    offset_int a = wi::to_offset (integer_one_node);

    // Create an invalid offset_int.  The function expects an INTEGER_CST
    // but doesn't do any input validation..
    offset_int b = wi::to_offset (x);

    // Trigger a SIGSEGV.
    if (a < b)
      return;
  }


Program received signal SIGSEGV, Segmentation fault.
0x00000000012f255e in selt (a=0x7fffffffcaf0, len=0, blocks_needed=2, small_prec=0, index=4294967295, sgn=SIGNED) at /ssd/src/gcc/svn/gcc/wide-int.cc:404
404	    val = SIGN_MASK (a[len - 1]);
(gdb) p len
$1 = 0
(gdb) bt
0x00000000012f255e in selt (a=0x7fffffffcaf0, len=0, blocks_needed=2, small_prec=0, index=4294967295, sgn=SIGNED) at /ssd/src/gcc/svn/gcc/wide-int.cc:404
404	    val = SIGN_MASK (a[len - 1]);
(gdb) bt
#0  0x00000000012f255e in selt (a=0x7fffffffcaf0, len=0, blocks_needed=2, small_prec=0, index=4294967295, sgn=SIGNED) at /ssd/src/gcc/svn/gcc/wide-int.cc:404
#1  0x00000000012f27c4 in wi::lts_p_large (op0=0x7fffffffcad0, op0len=1, precision=128, op1=0x7fffffffcaf0, op1len=0) at /ssd/src/gcc/svn/gcc/wide-int.cc:480
#2  0x0000000000866be1 in wi::lts_p<generic_wide_int<fixed_wide_int_storage<128> >, generic_wide_int<fixed_wide_int_storage<128> > > (x=..., y=...) at /ssd/src/gcc/svn/gcc/wide-int.h:1835
#3  0x00000000008664e5 in operator< <generic_wide_int<fixed_wide_int_storage<128> >, generic_wide_int<fixed_wide_int_storage<128> > > (x=..., y=...) at /ssd/src/gcc/svn/gcc/wide-int.h:3124
Comment 1 Richard Sandiford 2017-11-27 22:22:18 UTC
wi::to_offset does check for an INTEGER_CST, via the tree.h accessor macros.  Are you sure you have a normal --enable-checking=yes build?  I get:

cc1: internal compiler error: tree check: expected integer_cst, have trunc_div_expr in get_len, at tree.h:5315

with the quoted example.

But yeah, I've certainly no objection in principle to extra gcc_checking_asserts in wide-int.h itself.