GithubHelp home page GithubHelp logo

rems-project / cerberus Goto Github PK

View Code? Open in Web Editor NEW
34.0 34.0 12.0 149.83 MB

Cerberus C semantics

Home Page: https://www.cl.cam.ac.uk/~pes20/cerberus/

License: Other

Makefile 0.29% OCaml 45.86% Standard ML 0.05% C 1.42% Shell 0.41% Isabelle 6.65% TeX 0.11% Coq 12.47% JavaScript 0.92% TypeScript 1.10% CSS 0.22% sed 0.01% HTML 30.33% Python 0.14% Vim Script 0.01%

cerberus's People

Contributors

cp526 avatar dc-mak avatar elefthei avatar kerneis avatar kmemarian avatar kyndylan avatar mackieloeffel avatar metp avatar neel-krishnaswami avatar opqrs avatar petersewell avatar rbanerjee20 avatar ric-almeida avatar rlepigre avatar stellamplau avatar talsewell avatar victorgomes avatar vzaliva avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cerberus's Issues

Bug in Mem.kill

The concrete memory (and probably symbolic too) are incorrect for free(). Need to follow (§7.22.3.3#2):

if the argument does not match a pointer earlier returned by a memory management function, [...] the behavior is undefined.

Nested redefinition of struct/union and enums should be detected earlier

Consider:

struct T {
  struct T {int x;} *st;
};

right now the error seems to be detected when registering the definition of the outer struct T, this is tasteless...

Similarly for enums:

enum U { A, B = sizeof(enum U { C }) };

(in fact, as of writing, this fails at the sizeof() because the integer constant expression is too complicated)

Implicit conversion float to int

The code

void foo(int c) { }
int main() {
  foo(1.0);
}

fails with

OTHER ERROR: ill-formed program: `[t.c:4:3-11] ill-typed PEop ==> Ivmin("signed int") <= 1.'

Desugaring of objects with external linkage is all wrong

int f(void)
{
  extern int foo;
  return foo;
}

int main(void)
{
  extern void g(void);
  g();
  return f(); // this should return: 20
}

void g(void)
{
  extern int foo;
  foo = 20;
}

// if this line is commented, the program is UB because there is no definition
// of the external object "foo" even though it is used is an expression (§6.9#5)
int foo = 10;

Three things are wrong right now:

  1. the three declarations of foo should refer to the same object (§6.2.2#2)
  2. if the file scope definition of foo is commented, Cerberus should raise a UB
  3. the declaration of g() in main() refers to the following definition. (NOTE: fixing 1. will probably also fix this point)

NOTE(to self): I updated (in local copy) register_ordinary_identifier to do the right thing, but need to also change internal_register_identifier or the semantics of Scope_table.resolve because past external declarations need to always be visible to new external declarations, even when not technically in scope. But at the same time the normal scoping rule should still apply when the new declaration is not external...

Structs and function returning a pointer to a function

If someone writes this

struct S {
  void* (*f)(struct S* s);
};

void (*foo(struct S *p))(void){
  return p->f(p);
}

because why not! Cerberus complains with

foo.c:4:40: error: [desug] use of undeclared identifier 'p'. (§6.5.1#2)
void (*foo(struct S *p))(void){ return p->f(p); }

Defacto test: pointer_arith_algebraic_properties_2_auto.c

This should be allowed according to our current semantics, right?

#include <stdio.h>
#include <inttypes.h>
int main() {
  int y[2], x[2];
  int *p=(int*)(((uintptr_t)&(x[0])) + 
    (((uintptr_t)&(y[1]))-((uintptr_t)&(y[0]))));
  *p = 11;  // is this free of undefined behaviour?
  printf("x[1]=%d *p=%d\n",x[1],*p);
  return 0;
}

Cerberus is returning:

Undefined [pointer_arith_algebraic_properties_2_auto.c:7:3-5]{id: [UB043_indirection_invalid_value]}

Nested switch

Error on the desugaring to Ail:

int main()
{
  switch(0) {
    case 0:
      switch(0) {
        case 0:;
      }
    case 1:;
  }
}

gives the following error

switch4.c:8:5: error: [During desugaring] violation of constraint §6.8.1#2. (§6.8.1#2)
    case 1:;
    ^~~~~~~~ 

Can one use unsigned int* to point to an enum value?

Is the following code valid?

enum E {
  E0,
  E1
};

void foo(unsigned int *x) {}

int main() {
  enum E e = E0;
  foo(&e);
}

Cerberus complains saying that Ail is ill typed. Clang accepts with -Wall -Wextra -pedantic, although it rejects if we use a signed value int *x.

Typing error!

void foo(const char* x)
{
const char* y = x;
}

gives me

"One of the following shall hold:
— the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
— the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
— the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
— the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.". (§6.5.16.1#1)
const char* y = x;

Switch inside for loops

The code

int main() {
  for(;;) {
    switch (0) {
      case 0:;
    }
  }
}

fails with

foo.c:4:7: error: [desug] violation of constraint §6.8.1#2. (§6.8.1#2)
      case 0:;
      ^~~~~~~~ 

Cast to void

The code

int foo (void)
{
  return 0;
}

int main (void)
{
  (void) foo ();
}

fails with

cerberus: internal error, uncaught exception:
          Failure("Translation AilEcast, ptr vs ptr: just should be impossible")

Free NULL pointer

Cerberus runs this fine:

#include <stdlib.h>
int main ()
{
	char* buf= NULL;
	free(buf);/* Tool should detect this line as error */ /*ERROR:Freeing a NULL pointer*/
	buf = NULL;
}

static variables

Does Cerberus support static?

#include <stdio.h>
void foo() {
  static int x = 42;
  printf("%d\n", x);
  x++;
}
int main() {
  foo();
  foo();
}

prints 42\n42\n.

Array of array of chars

int main ()
{
    char str[][15] = {{"STRING"}, {"TEST"},{"STRING#"},{"TEST!"},{"TRIAL"}};
}

gives me

test.c:3:10: error: CoreTyping_TODO(illtyped Cspecified typecheck ==> pointer -- Specified(Array(conv_loaded_int("char", a_175), conv_loaded_int("char", a_176), conv_loaded_int("char", a_177), conv_loaded_int("char", a_178), conv_loaded_int("char", a_179), conv_loaded_int("char", a_180), conv_loaded_int("char", a_181), conv_loaded_int("char", a_182), conv_loaded_int("char", a_183), conv_loaded_int("char", a_184), conv_loaded_int("char", a_185), conv_loaded_int("char", a_186), conv_loaded_int("char", a_187), conv_loaded_int("char", a_188), conv_loaded_int("char", a_189)))). (Core typing error: TODO(msg) illtyped Cspecified typecheck ==> pointer -- Specified(Array(conv_loaded_int("char", a_175), conv_loaded_int("char", a_176), conv_loaded_int("char", a_177), conv_loaded_int("char", a_178), conv_loaded_int("char", a_179), conv_loaded_int("char", a_180), conv_loaded_int("char", a_181), conv_loaded_int("char", a_182), conv_loaded_int("char", a_183), conv_loaded_int("char", a_184), conv_loaded_int("char", a_185), conv_loaded_int("char", a_186), conv_loaded_int("char", a_187), conv_loaded_int("char", a_188), conv_loaded_int("char", a_189))))
    char str[][15] = {{"STRING"}, {"TEST"},{"STRING#"},{"TEST!"},{"TRIAL"}};

Returning a struct in a static function

The code

struct S { int x; };
static struct S f(void) { }

fails with

cerberus: internal error, uncaught exception:
          Failure("TODO: Cabs_to_ail_effect.register_ordinary_identifier, kind <> kind'")

Note that the code

struct S { int x; };
struct S f(void) { }

works as expected.

Cerberus internal error invalid arguments

The code

typedef struct
{
  short a;
  long b;
} S;

int main(void)
{
  S *a;
  S b;
  a = &b;
  *a = (S){1};
}

fails with

cerberus: internal error, uncaught exception:
          Invalid_argument("List.fold_left2")

Stack overflow

This is not necessarily an error, but it shows how easy it is to get an stack overflow in Cerberus.
If one sets the j to 1 or 2, it runs OK, otherwise stack overflow!

#include <stdlib.h>
#include <string.h>

typedef struct {
  int a[10];
} S;

int main()
{
  S* ptr_s1=malloc(15*sizeof(S));
  int j = 3;
  if(ptr_s1 != NULL) {
    for(int i=0;i<15;i++) {
      memset(ptr_s1,1,j*sizeof(S));
    }
  }
}

Loop in the evaluation...

There is a loop in the evaluation when running in exhaustive mode the following defacto test:
provenance_basic_global_yx.c

Initialising an array of arrays

This test (gcc-torture:20010114-1.c )

int
main (void)
{
  int array1[1] = { 1 };
  int array2[2][1]= { { 1 }, { 0 } };
  if (array1[0] != 1)
    abort ();
  exit (0);
}

was working before, but now it gives me

20010114-1.c:10:7: error: [Ail typing] (§6.5.4#2)
  "QUOTE NOT FOUND: §6.5.4#2". (§6.5.4#2)
  int array2[2][1]= { { 1 }, { 0 } };
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

The constraint does not make much sense.

Function pointer error!

void f (int s) { }
int main () {
void (*func)(int);
func = f;
}

cerberus: internal error, uncaught exception:
Failure("Translation.force_core_object_type_of_ctype MUST NOT be called on pointer to function ctype")

`offsetof` not working

The code

#include <stddef.h>
struct R {
  int x;
  char y;
};

int main() {
  offsetof(struct R, x);
}

fails with

foo.c:8:19: error: unexpected token 'struct'. (TODO: pp_errors std_ref)
  offsetof(struct R, x);

String with tabs

The string containing a TAB, fails with:

cerberus: internal error, uncaught exception:
          Failure("decode_character_constant: invalid char constant")

Overflow char

Is char signed? Shouldn't Cerberus spot this?

int main ()
{
  char max = 0x7f;
  char ret;
  ret = max + 1;/*Tool should detect this line as error*/ /*ERROR:Data Overflow*/
}

It does not spot even when using signed char.

Representation of long double

Any operation on long double throws:

cerberus: internal error, uncaught exception:
          File "src/concrete.ml", line 639, characters 6-12: Assertion failed
          
failed: bytes_of_int64(signed), i= 4666723172467343360, nbits= 128, [-170141183460469231731687303715884105728 ... 170141183460469231731687303715884105727]

calloc: UB025_misaligned_pointer_conversion

Any of the following lines:

long *buf=(long*) calloc(5,sizeof(long));
float *buf=(float*) calloc(5,sizeof(float));
double *buf=(double*) calloc(5,sizeof(double));
int **buf = (int**) calloc(5,sizeof(int*));
char ** doubleptr=(char**) malloc(10*sizeof(char*));

is raising the following error:

Undefined [test.c:470:2-45]{id: [UB025_misaligned_pointer_conversion]}

while these work fine:

char *buf=(char*) calloc(5,sizeof(char));
short *buf=(short*) calloc(5,sizeof(short));
int *buf=(int*) calloc(5,sizeof(int));

Underflow

Cerberus does not identify the underflow:

int main ()
{
	unsigned int min = 0;
	unsigned int ret;
	ret = min - 1;/*Tool should detect this line as error*/ /*ERROR:Data Underflow*/
}

and

int main ()
{
	char min = -128;	/* 0x80000002 */
	char ret;
	ret = min - 2;/*Tool should detect this line as error*/ /*ERROR:Data Underflow*/
}

Print a pointer using symbolic model

The following fail on (symbolic only):

#include <stdio.h>
int main()
{
  int y = 1;
  int *x = &y;
  printf("%p\n", (void*)x);
}

throws:

Undefined [t.c:6:10-16]{id: [DUMMY(rev_listFromStr_aux)]}

Is this a type checking error?

The Toyota test has the following construct in the test 12 (memory_allocation_failure.c):

void foo() {}
int main()
{
  int x = 0;
  (x == 0) ? (foo()) : (x=1);
}

Cerberus complains that this does not type check, foo() has type void and x=1 has type int.

I don't know if this is a problem with Cerberus or with the Toyota test. As far as I know, the standard says the following for the type of the conditional operator (6.5.15#5):

...If both operands have void type, the result has void type .

Wrong UB

The code

int main (void) {
  -1 << 31;
}

raises

Undefined [t.c:3:3-23]{id: [UB036_exceptional_condition]}

It should raise instead shift-negative-value.

Printing an unspecified pointer fails

#include <stdio.h>

int main(void)
{
  int *p;
  printf("%p\n", (void*)p);
}

gives the following error:

Killed {msg: MerrOther "store with an ill-typed memory value"}

Sum of unsigned and char*

I actually don't know if this code is well defined, but

void * a[255];
int main ()
{
  ((unsigned)0) + (char*)a;
}

fails with

cerberus: internal error, uncaught exception:
          Failure("Translation.usual_arithmetic_conversion ==> ty1= \027[32munsigned\027[0m int, ty2= pointer to \027[32mchar\027[0m, e1= \027[34ma_116\027[0m, e2= \027[34ma_117\027[0m")

Should AilEarray preserve qualifiers?

Consider:

int main(void)
{
  int *p;
  const int x[2];
  
  p = x; // is this a constraint violation (dicarding of const)?
}

Currently the right operand of the assignment is decayed to an AilEarray_decay during Ail's typing (as per §6.3.2.1#3) which is NOT an lvalue and therefore forgets about the const qualifier. So the typing of the assigment doesn't see any qualifiers on the right operand As result of this, cerberus accepts with program as well-typed.

Clang however gives a warning in pedantic mode (which makes more sense).

Union as value

The code

union U {
  int x;
  float y;
} u;

void f (union U u) { }

int main ()
{
  f (u);
}

fails with

cerberus: internal error, uncaught exception:
          Failure("TODO: combine_bytes, Union (as value)")

Elaboration of division is wrong!

int main() {
42/7;
}

elaborates to a case expression with too many options

proc main (): eff loaded integer :=
(let strong : loaded integer =
bound[0] (let weak (a_103: loaded integer, a_104: loaded integer) =
unseq(pure(Specified(42)), pure(Specified(7))) in
pure(case (a_103, a_104) of
| (Unspecified(
: ctype), : loaded integer) => undef(<<UB036_exceptional_condition>>)
| (
: loaded integer, Unspecified(_: ctype)) => undef(<<UB045a_division_by_zero>>)
| (Specified(a_105: integer), Specified(a_106: integer)) =>
let a_107: integer = conv_int("signed int", a_105) in
let a_108: integer = conv_int("signed int", a_106) in
if a_108 == 0 then
undef(<<UB045a_division_by_zero>>)
else
if is_representable_integer(conv_int("signed int", a_105) / a_108,
"signed int") then
Specified(catch_exceptional_condition("signed int",
conv_int("signed int", a_105) / a_108))
else
undef(<<UB045c_quotient_not_representable>>)
end)) in
pure(Unit) ;
skip) ;
(save ret_101: loaded integer (a_102: loaded integer:= Specified(0)) in
pure(a_102))

Nested struct initialisation

The code

struct symbol { char * name; };

struct block {
  struct symbol *sym;
};

int main(void)
{
  struct block a = { 0 };
}

fails with:

cerberus: internal error, uncaught exception:
          Failure("Global.fromJust(Core_Eval, PEstruct 2)")

Postincr + compound assignment

The program

int main()
{
  const char *s = "";
  char a[1];
  a[*s++] |= 1;
}

when running in exhaustive mode gives you

Killed {msg: MerrAccess Load [foo.c:5:5-9] OutOfBoundPtr}

or

Defined {value: "Specified(0)", stdout: "", blocked: "false"}

I'm guessing that the sequencing of the evaluation of s++ is wrong.

Struct init

The code

struct s { char *p; int t; };
int main()
{
  struct s *p = (& (struct s) { "hi", 1 });
}

Fails with

cerberus: internal error, uncaught exception:
          Failure("XXX E.return (AilEcompound ty a_e, GenRValue)")

Print a warning when multiple outputs

The only source of non determinism (that produces different results and it is not UB) is ==, ideally the user should be warn when executing in random mode.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.