[Bug sanitizer/81598] -fsanitize=enum does not detect range violation

2019-04-19 Thread julien.blanc at sprinte dot eu
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81598

Julien Blanc  changed:

   What|Removed |Added

 CC||julien.blanc at sprinte dot eu

--- Comment #9 from Julien Blanc  ---
I recently ran into this while trying to use -fsanitize=enum for the codebase.
The situation is worse for C++ enum class : since you can only assign them with
static_cast, no load check can be done (clang also fails to detect such cases).

Here’s a sample code that should trigger but does not :

#include 

enum class Foo
{
foo1 = 0,
foo2 = 1
};
std::ostream& operator<<(std::ostream& o, Foo foo)
{
switch(foo)
{
case Foo::foo1:
return o << "foo1";
case Foo::foo2:
return o << "foo2";
}
return o << "unknown";
}
int main()
{
Foo foo = static_cast(3);
std::cout << foo << std::endl;
return 0;
}


$ g++ -fsanitize=enum -fsanitize=undefined -fno-sanitize-recover enum.cpp 
$ ./a.out 
unknown
$

Note that clang++ is no better :

$ clang++-7 -fsanitize=enum -fsanitize=undefined -fno-sanitize-recover=all
enum.cpp 
$ ./a.out 
unknown
$

But i’d expect both checkers to detect such misuse. While it could be valid
code, there’s a high chance that its a bug. Ideally an attribute could be used
if it is expected behaviour.

[Bug sanitizer/81598] -fsanitize=enum does not detect range violation

2017-08-03 Thread marxin at gcc dot gnu.org
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81598

--- Comment #8 from Martin Liška  ---
(In reply to Jakub Jelinek from comment #7)
> As for the switch, I think there shouldn't be any runtime error, there is no
> UB.
> You can have default: label even if you list all the possible in-range
> cases, you can have enum values in between min/max that aren't in the
> enumeration, and you can have case labels outside of the enum range too,
> that is something for a warning, but not a runtime error.

I've just noticed that in this example:

cat enum2.c
int main(int argc, char **argv)
{
  char c = (char)argc;

  bool x = argc;

  switch (c)
  {
  case -128 ... 127:
break;
  default:
return 2;
  }

  switch (x)
  {
case true:
  return 2;
case false:
  return 3;
default:
  return 4;
  }
}

We are able to simplify the second switch in vrp:

[...]
Folding statement: switch (x_4)  [33.33%], case 0:  [33.33%],
case 1:  [33.33%]>
removing unreachable case label
[...]

But we can't do it for the first switch. That's probably missing optimization.
I'm interested in the case where we've got call enum values covered and default
is provided: would it be valid to
simply remove the block (via __builtin_unreachable or similarly)?

[Bug sanitizer/81598] -fsanitize=enum does not detect range violation

2017-08-03 Thread jakub at gcc dot gnu.org
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81598

--- Comment #7 from Jakub Jelinek  ---
As for the switch, I think there shouldn't be any runtime error, there is no
UB.
You can have default: label even if you list all the possible in-range cases,
you can have enum values in between min/max that aren't in the enumeration, and
you can have case labels outside of the enum range too, that is something for a
warning, but not a runtime error.

[Bug sanitizer/81598] -fsanitize=enum does not detect range violation

2017-08-03 Thread jakub at gcc dot gnu.org
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81598

--- Comment #6 from Jakub Jelinek  ---
The extra instrumention should be on NOP_EXPR/CONVERT_EXPR (dunno about
VIEW_CONVERT_EXPR) from some type to enum type if the from type isn't the same
enum type or some type with equal min/max values.  But given that we consider
such conversions useless:
  /* If both the inner and outer types are integral types, then the
 conversion is not necessary if they have the same mode and
 signedness and precision, and both or neither are boolean.  */
we'd need to instrument that early (either during genericization, or
gimplification at latest).  It could be done by turning the check into some ifn
and lowering it later (sanopt) of course.

[Bug sanitizer/81598] -fsanitize=enum does not detect range violation

2017-08-03 Thread marxin at gcc dot gnu.org
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81598

--- Comment #5 from Martin Liška  ---
(In reply to Marek Polacek from comment #4)
> But you get compile-time warnings for that (-Wswitch), no?

For:

cat enum.c
enum values
{
  A = 1000,
  B = 30,
  C = 100
};

int main(int argc, char **argv)
{
  enum values x = (enum values)argc;
  if (x == 12345)
return 1;

  switch (x)
  {
case A:
  return 1;
case B:
  return 1;
case C:
  return 2;
case 123:
  return 3;
default:
  return 4;
  }
}

I only have warning for 'case 123':

gcc -fsanitize=enum enum.c -Wall 
enum.c: In function ‘main’:
enum.c:22:5: warning: case value ‘123’ not in enumerated type ‘enum values’
[-Wswitch]
 case 123:
 ^~~~

But one could also produce warning for the if comparison and default bb in the
switch statement.

[Bug sanitizer/81598] -fsanitize=enum does not detect range violation

2017-08-03 Thread mpolacek at gcc dot gnu.org
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81598

--- Comment #4 from Marek Polacek  ---
But you get compile-time warnings for that (-Wswitch), no?

[Bug sanitizer/81598] -fsanitize=enum does not detect range violation

2017-08-03 Thread marxin at gcc dot gnu.org
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81598

Martin Liška  changed:

   What|Removed |Added

 Status|UNCONFIRMED |NEW
   Last reconfirmed||2017-08-03
 CC||mpolacek at gcc dot gnu.org
 Ever confirmed|0   |1
   Severity|normal  |enhancement

--- Comment #3 from Martin Liška  ---
I believe we can instrument more in order to catch more situations:

enum values
{
  A = 1000,
  B = 30,
  C = 100
};

enum values g;

int main(int argc, char **argv)
{
  enum values x = (enum values)argc;
  if (x == 12345)
return 1;

  return g;

  switch (x)
  {
case A:
  return 1;
case C:
  return 2;
case 123:
  return 3;
  }
}

In this case, CFG is based on values that are undefined. As I've been working
on research of switch statement, I noticed that it's very common that switch
covers all possibly values of an enumeral type. Having that, we can instrument
default label with some UBSAN call.

[Bug sanitizer/81598] -fsanitize=enum does not detect range violation

2017-07-28 Thread tim.ruehsen at gmx dot de
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81598

--- Comment #2 from Tim Ruehsen  ---
(In reply to Jakub Jelinek from comment #1)
> This isn't a load, it is a cast, we sanitize just loads from memory.

Hmmm, seems ok if the compiler doesn't warn.
But the sanitizer IMO should trigger.

What if this cast has been done in a function returning flag_t ?
This could even be buggy code in an external library.
Then the sanitizer should definitely trigger.
And it doesn't with this code:

#include 

typedef enum {
FLAG1 = (1 << 0),
FLAG2 = (1 << 1),
} flag_t;

static flag_t setter(int x)
{
return (flag_t) x;
}

int main(void)
{
int x = 5;
flag_t flags = setter(x);

printf("flags = %X\n", flags);

return 0;
}

$ g++-7 -O0 -fsanitize=undefined -fsanitize=enum -fno-sanitize-recover
enum_undef.cc
$ ./a.out
flags = 5

Hopefully, -O0 doesn't optimize into a single cast.

[Bug sanitizer/81598] -fsanitize=enum does not detect range violation

2017-07-28 Thread jakub at gcc dot gnu.org
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81598

--- Comment #1 from Jakub Jelinek  ---
This isn't a load, it is a cast, we sanitize just loads from memory.

>From the almost non-existent testsuite coverage in LLVM it is hard to find out
what exactly they decided to implement (-fsanitize=enum has a single testcase
with a single memory load there, -fsanitize=bool 2 testcases with a single
memory load each).