rems-project / cerberus Goto Github PK
View Code? Open in Web Editor NEWCerberus C semantics
Home Page: https://www.cl.cam.ac.uk/~pes20/cerberus/
License: Other
Cerberus C semantics
Home Page: https://www.cl.cam.ac.uk/~pes20/cerberus/
License: Other
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.
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)
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.'
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:
foo
is commented, Cerberus should raise a UBg()
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...
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); }
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]}
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:;
^~~~~~~~
The code
int f(void)
{
int x[8] = {0};
}
should initialise the entire array to 0
according to 6.7.9#19.
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
.
7.22.3.4#3 says:
The malloc function returns either a null pointer or a pointer to the allocated space.
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;
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:;
^~~~~~~~
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")
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;
}
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
.
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"}};
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.
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")
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));
}
}
}
There is a loop in the evaluation when running in exhaustive mode the following defacto test:
provenance_basic_global_yx.c
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.
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")
The code
int main(void)
{
char *t = "012345678";
*(int*)t;
}
fails with
TODO(pretty ub) ==> UB025_misaligned_pointer_conversion
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);
The string containing a TAB, fails with:
cerberus: internal error, uncaught exception:
Failure("decode_character_constant: invalid char constant")
I don't understand if it is indeed properly doing backtracking ... (this is only used by the random mode)
Two things are wrong:
See Cabs_to_ail.extract_program
.
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
.
The code
struct S {
int (*f)(int x, int (*g)(int x));
};
fails with
foo.c:2:32: error: redeclaration of 'x'. (TODO: pp_errors std_ref)
int (*f)(int x, int (*g)(int x));
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]
Example program:
struct S { char x; };
int foo()
{
if (&((struct a *)0)->b)
return 1;
return 0;
}
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));
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*/
}
int (*func())[4]
{
return 0;
}
gives me
unknown location error: [During desugaring] violation of constraint §6.7.6.3#1. (§6.7.6.3#1)
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)]}
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 .
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
.
#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"}
See Ocaml_implementation: as a temporary hack, float
and long double
are implemented as if they were double
.
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")
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).
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)")
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))
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)")
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.
int main(void)
{
extern int foo;
sizeof(foo);
}
this should be well defined (§6.9#5), but currently the desugaring behaves as if the foo
inside the sizeof()
is evaluated.
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)")
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.
The code
struct S {
int x;
};
struct T {
struct S *p;
};
struct S s;
struct T t[10] = { &s };
fails with
cerberus: internal error, uncaught exception:
Failure("WIP: Translation AilEconst, ConstantStruct")
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.