goblint / analyzer

Static analysis framework for C
https://goblint.in.tum.de
MIT License
175 stars 75 forks source link

Transformation `assert` creates parsing errors #1129

Open J2000A opened 1 year ago

J2000A commented 1 year ago

During the implementation for my bachelor thesis about generating test cases for the incremental analysis I encountered a problem: When using the transformation assert sometimes cil files are created that result in parsing errors. @stilscher

Some errors seem to be related to structs: Reproduce by: ./goblint tests/regression/09-regions/36-global_init_rc.c --enable trans.goblint-check --set trans.activated '["assert"]' --set trans.output transformed.c && ./goblint transformed.c Cil creates

  __goblint_check(p == & p);
  __goblint_check(p->next == 0);

but it should be

  __goblint_check(&p == & p);
  __goblint_check((&p)->next == 0);

Error Message:

temp/p_0_check.c:1764: Error: Invalid operands to binary operator: ==(Lval(Var(p,
                                                                               NoOffset)),
                                                                      AddrOf(Var(p,
                                                                                 NoOffset)))
temp/p_0_check.c:1765: Error: expecting a pointer to a struct
...
Error: There were parsing errors in .goblint/preprocessed/p_0_check.i
Fatal error: exception Goblint_lib__Maingoblint.FrontendError("Errormsg.Error")

Some seem to be related to the declaration and access of arrays: Reproduce by: ./goblint tests/regression/11-heap/09-no_strong_update_not_unique_thread.c --enable trans.goblint-check --set trans.activated '["assert"]' --set trans.output transformed.c && ./goblint transformed.c

  {
  pthread_t tids[argc] ;
  }
  {
  ...
    pthread_create((pthread_t * __restrict  )(& tids[i]), (pthread_attr_t const   * __restrict  )((void *)0),
                   & thread, (void * __restrict  )((void *)0));
  ...
  }

but it should be

  {
  pthread_t tids[argc] ;
  ...
    pthread_create((pthread_t * __restrict  )(& tids[i]), (pthread_attr_t const   * __restrict  )((void *)0),
                   & thread, (void * __restrict  )((void *)0));
  ...
  }

Error Message:

temp/p_0_check.c:1600: Error: Cannot resolve variable tids.
temp/p_0_check.c:1600: Error: Expecting a pointer type in index:
 t1=TInt(int, )
t2=TInt(int, )

temp/p_0_check.c:1600: Error: Expected lval for ADDROF. Got Const(Int(0,int,None))
...
Error: There were parsing errors in .goblint/preprocessed/p_0_check.i
Fatal error: exception Goblint_lib__Maingoblint.FrontendError("Errormsg.Error")

Analogously the problems are found when running the mutation generator on the following regression tests (Warning: The error may only occur on a mutated file. Thus, not all of theses errors can be reproduced without the mutation generator):

### Exception on original program
Error: expecting a pointer to a struct
Error: Invalid operands to binary operator: ==(Lval(Var(p,
                                                    NoOffset)),
                                            AddrOf(Var(p,
                                                        NoOffset)))
../../analyzer/tests/regression/09-regions/36-global_init_rc.c
../../analyzer/tests/regression/09-regions/37-global_init_nr.c

### Exception on original program
Error: Expecting a pointer type in index:
 t1=TInt(int, )
t2=TInt(int, )
Error: Expected lval for ADDROF. Got Const(Int(0,int,None))
../../analyzer/tests/regression/11-heap/09-no_strong_update_not_unique_thread.c
../../analyzer/tests/regression/66-interval-set-one/96-more_passing.c

### Exception on original program
Error: expecting a struct with field x
Error: expecting a struct with field y
Expected an lval in MEMBEROF (field x)
../../analyzer/tests/regression/21-casts/01-via_ptr.c

### Exception on original program
Error: Expecting a pointer type in index:
 t1=TInt(int, )
t2=TInt(int, )
../../analyzer/tests/regression/23-partitioned_arrays_last/08-unsupported.c

### Exception on original program
temp/p_0_check.c:1238: Error: Expecting a pointer type in index:
 t1=TInt(int, )
t2=TInt(int, )
Expected lval for assignment. Got Const(Int(0,int,None))
../../analyzer/tests/regression/25-vla/02-loop.c
../../analyzer/tests/regression/25-vla/06-even_more_passing.c
../../analyzer/tests/regression/26-undefined_behavior/05-dynamically-sized-array-oob-access.c
../../analyzer/tests/regression/42-annotated-precision/05-array.c
../../analyzer/tests/regression/42-annotated-precision/22-26_05-dynamically-sized-array-oob-access.c
../../analyzer/tests/regression/66-interval-set-one/01-dynamically-sized-array-oob-access.c
../../analyzer/tests/regression/66-interval-set-one/69-even_more_passing.c
../../analyzer/tests/regression/67-interval-sets-two/13-loop.c

### Exception on original program
old type = TFun(TVoid(), n: TInt(int, ),
                a: TPtr(TArray(TInt(int, ), Some(Lval(Var(n, NoOffset))), ), ), )
new type = TFun(TVoid(), n: TInt(int, ),
                a: TPtr(TArray(TInt(int, ), Some(Lval(Var(n, NoOffset))), ), ), )
temp/p_0_check.c:901: Error: Declaration of foo2 does not match previous declaration from temp/p_0_check.c:318 (different array lengths).
error in createGlobal(foo2: temp/p_0_check.c:901): GoblintCil__Errormsg.Errortemp/p_0_check.c:1257: Error: Cannot resolve variable b.
temp/p_0_check.c:1276: Warning: Array type comparison succeeds based on being lenient for funargs, proceed with caution: n n
../../analyzer/tests/regression/25-vla/03-calls.c
../../analyzer/tests/regression/66-interval-set-one/39-calls.c

### Exception on original program
Array type comparison succeeds based on being lenient for funargs, proceed with caution: m m
Expecting a pointer type in index:
 t1=TInt(int, )
t2=TInt(int, )
Expected lval for assignment. Got Const(Int(0,int,None))
../../analyzer/tests/regression/25-vla/04-passing_ptr_to_array.c
../../analyzer/tests/regression/66-interval-set-one/57-passing_ptr_to_array.c

### Exception on original program
Warning: Array type comparison succeeds based on being lenient for funargs, proceed with caution: n n
Warning: Array type comparison succeeds based on being lenient for funargs, proceed with caution: *(b + 0) *(b + 0)
Expecting a pointer type in index:
 t1=TInt(int, )
t2=TInt(int, )
Error: Expected lval for assignment. Got Const(Int(0,int,None))
../../analyzer/tests/regression/25-vla/05-more_passing.c

### Exception on original program
Error: Cannot resolve variable a.
../../analyzer/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c
michael-schwarz commented 1 year ago

Thank you for this report! For it to be within scope here, the programs would need to actually compile with, e.g., gcc. Did you check whether this is the case? Otherwise I'd suggest to transfer it to the goblint repo, as the assert transformation is implemented there.

J2000A commented 1 year ago

Hello @michael-schwarz,

the input programs do compile with gcc: gcc tests/regression/09-regions/36-global_init_rc.c -c -w runs without problems

but the transfored ones do not: ./goblint tests/regression/09-regions/36-global_init_rc.c --enable trans.goblint-check --set trans.activated '["assert"]' --set trans.output transformed.c gcc transformed.c -c -w results in

transformed.c:1440:42: error: ‘reallocarray’ undeclared here (not in a function)
 1440 | __malloc__(__builtin_free,1), __malloc__(reallocarray,1), __alloc_size__(2,3))) ;
      |                                          ^~~~~~~~~~~~
transformed.c: In function ‘main’:
transformed.c:1763:21: error: invalid operands to binary == (have ‘struct s’ and ‘struct s *’)
 1763 |   __goblint_check(p == & p);
      |                     ^~ ~~~
      |                        |
      |                        struct s *
transformed.c:1764:20: error: invalid type argument of ‘->’ (have ‘struct s’)
 1764 |   __goblint_check(p->next == 0);
      |                    ^~
transformed.c:1766:21: error: invalid operands to binary == (have ‘struct s’ and ‘struct s *’)
 1766 |   __goblint_check(p == & p);
      |                     ^~ ~~~
      |                        |
      |                        struct s *
transformed.c:1767:20: error: invalid type argument of ‘->’ (have ‘struct s’)
 1767 |   __goblint_check(p->next == 0);
      |                    ^~
transformed.c:1769:21: error: invalid operands to binary == (have ‘struct s’ and ‘struct s *’)
 1769 |   __goblint_check(p == & p);
      |                     ^~ ~~~
      |                        |
      |                        struct s *
transformed.c:1770:20: error: invalid type argument of ‘->’ (have ‘struct s’)
 1770 |   __goblint_check(p->next == 0);
      |                    ^~
transformed.c:1772:21: error: invalid operands to binary == (have ‘struct s’ and ‘struct s *’)
 1772 |   __goblint_check(p == & p);
      |                     ^~ ~~~
      |                        |
      |                        struct s *
transformed.c:1773:20: error: invalid type argument of ‘->’ (have ‘struct s’)
 1773 |   __goblint_check(p->next == 0);
      |                    ^~
transformed.c:1775:21: error: invalid operands to binary == (have ‘struct s’ and ‘struct s *’)
 1775 |   __goblint_check(p == & p);
      |                     ^~ ~~~
      |                        |
      |                        struct s *
transformed.c:1776:20: error: invalid type argument of ‘->’ (have ‘struct s’)
 1776 |   __goblint_check(p->next == 0);
      |                    ^~
transformed.c:1778:21: error: invalid operands to binary == (have ‘struct s’ and ‘struct s *’)
 1778 |   __goblint_check(p == & p);
      |                     ^~ ~~~
      |                        |
      |                        struct s *
transformed.c:1779:20: error: invalid type argument of ‘->’ (have ‘struct s’)
 1779 |   __goblint_check(p->next == 0);
      |                    ^~
transformed.c:1781:21: error: invalid operands to binary == (have ‘struct s’ and ‘struct s *’)
 1781 |   __goblint_check(p == & p);
      |                     ^~ ~~~
      |                        |
      |                        struct s *
transformed.c:1782:20: error: invalid type argument of ‘->’ (have ‘struct s’)
 1782 |   __goblint_check(p->next == 0);
      |                    ^~
michael-schwarz commented 1 year ago

Thanks for the quick reply! Yes, but this seems like it is the transformation that causes the issue right? So it's less of an issue with CIL rejecting valid code, and more with the transformation in Goblint producing incorrect code, and as such an issue with Goblint?

sim642 commented 1 year ago

These might be issues with the invariant expression generation from our domains. We sometimes have representations which don't match the types precisely, so naively dumping them could cause problems like this.