[2/9] Use unsigned as base type for some enums

Message ID 20180827145620.11055-3-tom@tromey.com
State Superseded
Headers show
Series
  • Add UBSan to the build
Related show

Commit Message

Tom Tromey Aug. 27, 2018, 2:56 p.m.
-fsanitize=undefined complains about using operator~ on various enum
types that are used with DEF_ENUM_FLAGS_TYPE.  This patch fixes these
problems by explicitly setting the base type for these enums to
unsigned.

ChangeLog
2018-08-27  Tom Tromey  <tom@tromey.com>

	* symfile-add-flags.h (enum symfile_add_flag): Use unsigned as
	base type.
	* objfile-flags.h (enum objfile_flag): Use unsigned as base type.
	* gdbtypes.h (enum type_instance_flag_value): Use unsigned as base
	type.
	* c-lang.h (enum c_string_type_values): Use unsigned as base
	type.
	* btrace.h (enum btrace_thread_flag): Use unsigned as base type.
---
 gdb/ChangeLog           | 11 +++++++++++
 gdb/btrace.h            |  2 +-
 gdb/c-lang.h            |  2 +-
 gdb/gdbtypes.h          |  2 +-
 gdb/objfile-flags.h     |  2 +-
 gdb/symfile-add-flags.h |  2 +-
 6 files changed, 16 insertions(+), 5 deletions(-)

-- 
2.13.6

Comments

Simon Marchi Aug. 27, 2018, 7:21 p.m. | #1
On 2018-08-27 10:56 AM, Tom Tromey wrote:
> -fsanitize=undefined complains about using operator~ on various enum

> types that are used with DEF_ENUM_FLAGS_TYPE.  This patch fixes these

> problems by explicitly setting the base type for these enums to

> unsigned.


Can you give an example of how the error manifests itself (I'm not really
familiar with -fsanitize=undefined).  Is the error reported at compile-time
or run-time?  I'm not able to make a synthetic standalone example to reproduce
the error.

In any case, that LGTM if that makes the compiler happy.  If the error reported
by -fsanitize=undefined is at run-time, could we add a static assert in there
to make sure the underlying types of types used with DEF_ENUM_FLAGS_TYPE are
unsigned, to get a compilation error?

Simon
Tom Tromey Aug. 27, 2018, 8:21 p.m. | #2
>>>>> "Simon" == Simon Marchi <simon.marchi@ericsson.com> writes:


Simon> Can you give an example of how the error manifests itself (I'm not really
Simon> familiar with -fsanitize=undefined).  Is the error reported at compile-time
Simon> or run-time?  I'm not able to make a synthetic standalone example to reproduce
Simon> the error.

You will get an error at runtime, and with the flags added by the last
patch in the series, a crash.

The error looks somewhat like the error from the expression dumping
patch:

  runtime error: load of value 2887952, which is not a valid value for type 'exp_opcode'

(I don't have an exact error handy, this was just taken from the other
patch.)

Simon> In any case, that LGTM if that makes the compiler happy.  If the error reported
Simon> by -fsanitize=undefined is at run-time, could we add a static assert in there
Simon> to make sure the underlying types of types used with DEF_ENUM_FLAGS_TYPE are
Simon> unsigned, to get a compilation error?

With the final patch, any UB will cause gdb to crash (in development
mode), presumably leading to a test suite failure.  I think it isn't
necessary to require unsigned as the underlying type -- any type will
do.  However I don't know how to assert that.

Tom
Simon Marchi Aug. 27, 2018, 9:26 p.m. | #3
On 2018-08-27 04:21 PM, Tom Tromey wrote:
>>>>>> "Simon" == Simon Marchi <simon.marchi@ericsson.com> writes:

> 

> Simon> Can you give an example of how the error manifests itself (I'm not really

> Simon> familiar with -fsanitize=undefined).  Is the error reported at compile-time

> Simon> or run-time?  I'm not able to make a synthetic standalone example to reproduce

> Simon> the error.

> 

> You will get an error at runtime, and with the flags added by the last

> patch in the series, a crash.

> 

> The error looks somewhat like the error from the expression dumping

> patch:

> 

>   runtime error: load of value 2887952, which is not a valid value for type 'exp_opcode'

> 

> (I don't have an exact error handy, this was just taken from the other

> patch.)

> 

> Simon> In any case, that LGTM if that makes the compiler happy.  If the error reported

> Simon> by -fsanitize=undefined is at run-time, could we add a static assert in there

> Simon> to make sure the underlying types of types used with DEF_ENUM_FLAGS_TYPE are

> Simon> unsigned, to get a compilation error?

> 

> With the final patch, any UB will cause gdb to crash (in development

> mode), presumably leading to a test suite failure.  I think it isn't

> necessary to require unsigned as the underlying type -- any type will

> do.  However I don't know how to assert that.


Indeed, it's only necessary if the ~ operator is used.  OTOH, it doesn't really
make sense to use a signed type for flags, so we wouldn't lose anything by enforcing
unsigned types.  If I understand correctly, the errors come from code like this, when
making a bit mask to clear some bits:

  btinfo->flags &= ~(BTHR_MOVE | BTHR_STOP);

Doing a static assert like this:

diff --git a/gdb/common/enum-flags.h b/gdb/common/enum-flags.h
index 82568a5..c82970c 100644
--- a/gdb/common/enum-flags.h
+++ b/gdb/common/enum-flags.h
@@ -92,6 +92,7 @@ class enum_flags
 public:
   typedef E enum_type;
   typedef typename enum_underlying_type<enum_type>::type underlying_type;
+  gdb_static_assert (std::is_unsigned<underlying_type>::value);

 private:
   /* Private type used to support initializing flag types with zero:


would enforce it at compile time, which is preferable than finding it at
runtime.

Simon
Pedro Alves Aug. 28, 2018, 6:54 p.m. | #4
On 08/27/2018 10:26 PM, Simon Marchi wrote:
> On 2018-08-27 04:21 PM, Tom Tromey wrote:

>>>>>>> "Simon" == Simon Marchi <simon.marchi@ericsson.com> writes:

>>

>> Simon> Can you give an example of how the error manifests itself (I'm not really

>> Simon> familiar with -fsanitize=undefined).  Is the error reported at compile-time

>> Simon> or run-time?  I'm not able to make a synthetic standalone example to reproduce

>> Simon> the error.

>>

>> You will get an error at runtime, and with the flags added by the last

>> patch in the series, a crash.

>>

>> The error looks somewhat like the error from the expression dumping

>> patch:

>>

>>   runtime error: load of value 2887952, which is not a valid value for type 'exp_opcode'

>>

>> (I don't have an exact error handy, this was just taken from the other

>> patch.)

>>

>> Simon> In any case, that LGTM if that makes the compiler happy.  If the error reported

>> Simon> by -fsanitize=undefined is at run-time, could we add a static assert in there

>> Simon> to make sure the underlying types of types used with DEF_ENUM_FLAGS_TYPE are

>> Simon> unsigned, to get a compilation error?

>>

>> With the final patch, any UB will cause gdb to crash (in development

>> mode), presumably leading to a test suite failure.  I think it isn't

>> necessary to require unsigned as the underlying type -- any type will

>> do.  However I don't know how to assert that.

> 

> Indeed, it's only necessary if the ~ operator is used.  OTOH, it doesn't really

> make sense to use a signed type for flags, so we wouldn't lose anything by enforcing

> unsigned types.  If I understand correctly, the errors come from code like this, when

> making a bit mask to clear some bits:

> 

>   btinfo->flags &= ~(BTHR_MOVE | BTHR_STOP);

> 

> Doing a static assert like this:

> 

> diff --git a/gdb/common/enum-flags.h b/gdb/common/enum-flags.h

> index 82568a5..c82970c 100644

> --- a/gdb/common/enum-flags.h

> +++ b/gdb/common/enum-flags.h

> @@ -92,6 +92,7 @@ class enum_flags

>  public:

>    typedef E enum_type;

>    typedef typename enum_underlying_type<enum_type>::type underlying_type;

> +  gdb_static_assert (std::is_unsigned<underlying_type>::value);

> 


Yeah.  

I've been meaning to find time to repost by enum-flags v2 fixes,
but, it's never happened...  One of the things that I wanted to
look at is exactly this issue, seeing about coming up with some
solution that is compatible with C++98/C++03, given that the GCC
folks expressed interest in sharing the enum-flags.h header.
Off hand, all I can think of is to introduce a "clear(unsigned mask)"
method that would be used instead of &= and ~...  But maybe really
best is to ignore that (C++98), expecting that GCC will come around
to requiring C++11 in some reasonable timeframe...

>  private:

>    /* Private type used to support initializing flag types with zero:

> 

> 

> would enforce it at compile time, which is preferable than finding it at

> runtime.

Yes, I think that we should add that.

Thanks,
Pedro Alves
Tom Tromey Aug. 28, 2018, 10:41 p.m. | #5
>>>>> "Simon" == Simon Marchi <simon.marchi@ericsson.com> writes:


Simon> Doing a static assert like this:

Simon> +  gdb_static_assert (std::is_unsigned<underlying_type>::value);

Simon> would enforce it at compile time, which is preferable than finding it at
Simon> runtime.

I made this change.

Tom
Tom Tromey Aug. 28, 2018, 11:51 p.m. | #6
>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:


>>>>> "Simon" == Simon Marchi <simon.marchi@ericsson.com> writes:

Simon> Doing a static assert like this:

Simon> +  gdb_static_assert (std::is_unsigned<underlying_type>::value);

Simon> would enforce it at compile time, which is preferable than finding it at
Simon> runtime.

Tom> I made this change.

This change turns out to be a bit too eager.  It requires changing all
the enums that are used by DEF_ENUM_FLAGS_TYPE; including
gcc_qualifiers, which is maintained in gcc (and theoretically at least
is C compatible) -- but operator~ is never actually used with these
other enums, so there isn't any UB to avoid.

However, moving the static assert to operator~ seems to work.

Tom

Patch

diff --git a/gdb/btrace.h b/gdb/btrace.h
index bfb0b9f2787..0448bd6d498 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -228,7 +228,7 @@  struct btrace_call_history
 };
 
 /* Branch trace thread flags.  */
-enum btrace_thread_flag
+enum btrace_thread_flag : unsigned
 {
   /* The thread is to be stepped forwards.  */
   BTHR_STEP = (1 << 0),
diff --git a/gdb/c-lang.h b/gdb/c-lang.h
index b7bf08992b9..a6d5753a287 100644
--- a/gdb/c-lang.h
+++ b/gdb/c-lang.h
@@ -35,7 +35,7 @@  struct parser_state;
 /* The various kinds of C string and character.  Note that these
    values are chosen so that they may be or'd together in certain
    ways.  */
-enum c_string_type_values
+enum c_string_type_values : unsigned
   {
     /* An ordinary string: "value".  */
     C_STRING = 0,
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 14059ab3dc5..f7ea2ac6df5 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -192,7 +192,7 @@  enum type_code
 /* * Some bits for the type's instance_flags word.  See the macros
    below for documentation on each bit.  */
 
-enum type_instance_flag_value
+enum type_instance_flag_value : unsigned
 {
   TYPE_INSTANCE_FLAG_CONST = (1 << 0),
   TYPE_INSTANCE_FLAG_VOLATILE = (1 << 1),
diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h
index aeaa8fbc04d..6f5760d021d 100644
--- a/gdb/objfile-flags.h
+++ b/gdb/objfile-flags.h
@@ -25,7 +25,7 @@ 
 /* Defines for the objfile flags field.  Defined in a separate file to
    break circular header dependencies.  */
 
-enum objfile_flag
+enum objfile_flag : unsigned
   {
     /* When an object file has its functions reordered (currently
        Irix-5.2 shared libraries exhibit this behaviour), we will need
diff --git a/gdb/symfile-add-flags.h b/gdb/symfile-add-flags.h
index 3c07513e395..35241fe4a04 100644
--- a/gdb/symfile-add-flags.h
+++ b/gdb/symfile-add-flags.h
@@ -26,7 +26,7 @@ 
    symbol_file_add, etc.  Defined in a separate file to break circular
    header dependencies.  */
 
-enum symfile_add_flag
+enum symfile_add_flag : unsigned
   {
     /* Be chatty about what you are doing.  */
     SYMFILE_VERBOSE = 1 << 1,