• Keine Ergebnisse gefunden

Handling of Arrays as Input Parameters

5.11 Handling of Arrays

5.11.1 Handling of Arrays as Input Parameters

As we have already mentioned, the input parameters of array type are represented by GIMPLE as pointers and array reads as a dereferenced pointer after the addition of a corresponding offset. The handling of dereferenced pointers is discussed in Section 5.6. This approach is working well for cases when the dereferenced pointer can be resolved up to a concrete array element, so that only its atomic value has to be determined. However, when the dereferenced pointer refers to an input array and the offset depends on an input, the discussed algorithm is not sufficient. In this section we will, therefore, extend it, so that it will be able to handle array inputs with input indices.

First, we illustrate by an example where the algorithm discussed in Section 5.6 reaches its limits. The module under testtest()compares two elements of the integer arrayaand returnstrueif the element a[x]is greater then the elementa[y] and f alseotherwise. To ensure that passed values ofxandy are within the array bounds, we restrict their range by a precondition.

C code GIMPLE representation

1 # d e f i n e N 2

2 t y p e d e f i n t m y _ a r r a y [N ] ; 3 i n t t e s t ( m y _ a r r a y a ,

4 u n s i g n e d i n t x ,

5 u n s i g n e d i n t y )

6 {

7 _ _ r t t _ p r e c o n d i t i o n( x < N && y < N) ; 8

9 i n t r e t v a l = 0 ;

10 i f( a [ x ] > a [ y ] ) {

11 r e t v a l = 1 ;

12 } e l s e {

13 r e t v a l = 0 ;

14 }

15 r e t u r n r e t v a l ;

16 }

1 i n t t e s t (i n t ∗a , i n t x ,

2 i n t y ) {

3 i n t D_1768 ;

4 i n t ∗D_1767 ;

5 u n s i g n e d i n t D_1766 ;

6 i n t D_1765 ;

7 i n t ∗D_1764 ;

8 u n s i g n e d i n t D_1763 ;

9 . . .

10 D_1763 = x 4 ;

11 D_1764 = a + D_1763 ;

12 D_1765 = ∗D_1764 ;

13 D_1766 = y 4 ;

14 D_1767 = a + D_1766 ;

15 D_1768 = ∗D_1767 ;

16 i f ( D_1765 > D_1768 ) {

17 . . .

18 }

19 }

We demonstrate here not the whole GIMPLE representation (and symbolic execution) but only the evaluation of the guard condition of theifstatement in line 10 of the C code and the symbolic execution of the relevant statements (the execution of statements involving the variableretvalas well as of the precondition is omitted). The complete generator output for this example is presented in Appendix 12.

For a better understanding of the procedure, we represent it as follows: we list the example code line by line and after each line we specify the memory items which were created by the symbolic execution of this line. The symbolic execution steps are numbered according to the line numbers of the GIMPLE representation listed above.

First the memory is initialized. Initialization of parameters:

1 i n t t e s t (i n t ∗a , i n t x , i n t y )

m1= (1,∞,&a, 0, 32, int*, &a@P[0]1,true)

m2= (1,∞,&a@P[0], 0, 3200, int[100], a@P0, true) m3= (2,∞,&x, 0, 32, unsigned int, x0,true)

m4= (3,∞,&y, 0, 32, unsigned int, y0,true)

For the parameter atwo memory items were created: m1 andm2, where m2 corresponds to an auxiliary arraya@P, which simulates the memory were the pointer parameterapoints to.

Subsequently, the stack initialization is done:

3 i n t D_1768 ;

m5= (4,∞,&D_1768, 0, 32, int, Undef, true)

4 i n t ∗D_1767 ;

m6= (5,∞,&D_1767, 0, 32, int*, Undef, true) 5 u n s i g n e d i n t D_1766 ;

m7= (6,∞,&D_1766, 0, 32, unsigned int, Undef, true) 6 i n t D_1765 ;

m8= (7,∞,&D_1765, 0, 32, int, Undef, true) 7 i n t ∗D_1764 ;

m9= (8,∞,&D_1764, 0, 32, int*, Undef, true) 8 u n s i g n e d i n t D_1763 ;

m10= (9,∞,&D_1763, 0, 32, unsigned int, Undef, true)

After the initialization is completed, we proceed with the symbolic execution line by line:

10 D_1763 = x 4 ;

m11= (10,∞,&D_1763, 0, 32, unsigned int, x10·4, true)

The insertion of the memory itemm11into the memory specification invalidates the memory item m10, so that nowm10is configured as follows:

m10= (9, 9, &D_1763, 0, 32, unsigned int, Undef, true)

11 D_1764 = a + D_1763 ;

m12= (11,∞,&D_1764, 0, 32, int*, a11+D_176311, true)

The insertion of the memory itemm12into the memory specification invalidates the memory item m9:

m9= (8, 10, &D_1764, 0, 32, int*, Undef, true) 12 D_1765 = D_1764 ;

m13= (12,∞,&D_1765, 0, 32, int, *D_176412, true)

The insertion of the memory itemm13into the memory specification invalidates the memory item m8:

m8= (7, 11, &D_1765, 0, 32, int, Undef, true)

13 D_1766 = y 4 ;

m14= (13,∞,&D_1766, 0, 32, unsigned int, y13·4, true)

The insertion of the memory itemm14into the memory specification invalidates the memory item m7:

m7= (6, 12, &D_1766, 0, 32, unsigned int, Undef, true)

14 D_1767 = a + D_1766 ;

m15= (14,∞,&D_1767, 0, 32, int*, a14+D_176614, true)

The insertion of the memory itemm15into the memory specification invalidates the memory item m6:

m6= (7, 13, &D_1767, 0, 32, int*, Undef, true) 15 D_1768 = D_1767 ;

m16= (15,∞,&D_1768, 0, 32, int, *D_176715, true)

The insertion of the memory itemm16into the memory specification invalidates the memory item m5:

m5= (4, 14, &D_1768, 0, 32, int, Undef, true)

The next line of the example consists of anifstatementif(D_1765 > D_1768). This means that the evaluation of the guard condition(D_1765 > D_1768)is necessary. Before we start with the resolution algorithm, we summarize the current memory specification:

m1= (1,∞,&a, 0, 32, int*, &a@P[0]1,true)

m2= (1,∞,&a@P[0], 0, 3200, int[100], a@P0, true) m3= (2,∞,&x, 0, 32, unsigned int, x0,true)

m4= (3,∞,&y, 0, 32, unsigned int, y0,true) m5= (4, 14, &D_1768, 0, 32, int, Undef, true) m6= (5, 13, &D_1767, 0, 32, int*, Undef, true)

m7= (6, 12, &D_1766, 0, 32, unsigned int, Undef, true) m8= (7, 11, &D_1765, 0, 32, int, Undef, true)

m9= (8, 10, &D_1764, 0, 32, int*, Undef, true)

m10= (9, 9, &D_1763, 0, 32, unsigned int, Undef, true) m11= (10,∞,&D_1763, 0, 32, unsigned int, x10·4, true) m12= (11,∞,&D_1764, 0, 32, int*, a11+D_176311, true) m13= (12,∞,&D_1765, 0, 32, int, *D_176412, true) m14= (13,∞,&D_1766, 0, 32, unsigned int, y13·4, true) m15= (14,∞,&D_1767, 0, 32, int*, a14+D_176614, true) m16= (15,∞,&D_1768, 0, 32, int, *D_176715, true)

Now we process as defined by the functionresolveConstraint()(Algorithm 11):

1. Initialize the path constraint according to the guard condition:

Φ= (D_176515>D_176815).

2. Resolve D_176515: find the memory item responsible for D_176515, this is m13. Resolve D_176515according to the value of the item found:

D_176515==*D_176412.

Now the algorithm resolveDerefPtr() (see Algorithm 15) is invoked with D_176515 as var,

*D_176412asp,Φascand our memory configuration asmem. It passes further resolution to the algorithmresolveDerefPtrExp()(see Algorithm 16). The resolution of the memory itemm12 by the functionresolvePtrVal()produces the following specification: the base address is&a@P[0]

and the offset isx0·4 (the value of the memory itemm11 is (x10·4),x10 is resolved up to the in-put). The internal loop finds matching memory itemm2. Now the algorithmresolveDerefPtrExp() invokes the subroutineresolveExp()(Algorithm 12), but the value (a@P0) of the found memory itemm2is an input and cannot be resolved further. However, neither this value can be passed to the solver in an expression like (D_176515==a@P0), since it is of an array type, so that the corresponding expression makes no sense. To be able to handle this situation, we must first extend the algorithmresolveDerefPtrExp().

Algorithm 27 shows the extended functionresolveDerefPtrExp() (the unextended version is shown in Algorithm 16). The part of the algorithm that was introduced for the handling of input arrays is highlighted in light gray. As input arrays are represented by GIMPLE as pointers and the information, if this is a pointer or an array, is lost, we represent all input pointers (except of pointers to union types) as arrays of a modifiable size. Since all array elements which have the same value can be represented as a single memory item, this generates merely a slight overhead. Now, if a pointer refers to an input, we can act on the assumption, that this pointer refers to an input array. In this case the array expression resolving the variablevarto the element of arraym.val at indexiby invocation ofrttArrayRead()is built.

i n p u t: var v a r i a b l e i d e n t i f i e r which h a s a d e r e f e r e n c e d p o i n t e r a s v a l u e p p o i n t e r i d e n t i f i e r

mem c u r r e n t memory s p e c i f i c a t i o n

validFrom i n d i c a t e s t h e v a l i d i t y p e r i o d o f m a t c h i n g memory i t e m s i n o u t: R s e t o f f o u n d r e s o l u t i o n s

isInput i n d i c a t e s w h e t h e r t h e r e s o l u t i o n i s p e r f o r m e d f o r a p o i n t e r i n p u t p r o c e d u r e r e s o l v e D e r e f P t r E x p (var, p, R, mem, isInput, validFrom) {

/ / f i n d o u t c o r r e s p o n d i n g s e g m e n t S=σ(β(p),mem);

offsetStart=ω(p);

offsetEnd=ω(p) + s i z e(b a s e t y p e(p) ) ; f o r e a c h m=last(S) downto head(S){

i f(m.v0υ(p)υ(p)m.v1m.a == β(p) ) { i f(m.val i s a p o i n t e r s t r u c t a c c e s s ) {

pl = r e s o l v e S t r u c t P t r V a l (m.val,mem) ; } e l s e {

pl = r e s o l v e P t r V a l (m.val,mem) ; }

f o r e a c h m i n pl{

/ / f o r e a c h memory i t e m s p e c i f i c a t i o n i n t h e l i s t / / f i n d a l l i t e m s o v e r l a p p i n g w i t h i t

S1=σ(m.a,mem);

f o r e a c h m=last(S1) downto head(S1){

i f( (isInput validFrom<m.v0 m r e f e r s t o a s i m u l a t e d i n p u t) !isInput)) { i f(m.v0υ(var)υ(var)m.v1m.a == m.a) {

i f(m.val i s n o t an i n p u t) {

overlap= (m.o<m.o+offsetEnd)(m.l>m.o+offsetStart);

c1=m.cm.cm.c∧overlap;

i f(c1 i s f e a s i b l e ) {

r e s o l v e E x p(var,m.val,c2,mem);

R. p u s h ( (c2, p,c1,m.v0) ) ;

i f(m r e f e r s t o a s i m u l a t e d i n p u t) isInput = true; }

} e l s e { υ(i) =υ(p);

idxExp= ((i== (m.o·8)/s i z e(b a s e t y p e(p)))∧(m.os i z e(b a s e t y p e (p))<m.l));

arrayExp= (var==r t t A r r a y R e a d (m.val, i));

c1=m.cm.cm.c; i f(c1 i s f e a s i b l e ) {

c2=arrayExpidxExp; R. p u s h ( (c2, p,c1,m.v0) ) ;

i f(m r e f e r s t o a s i m u l a t e d i n p u t) isInput = true; }

} } } } } }

The index expression holds the resolution expression for the index where the array read is performed. To build an index expression an auxiliary variableiis introduced. The version of this auxiliary variable is set equal to the version of the pointer identifier p, since the array read is made in the computational step corresponding to this version. The value of the index is required to be equal to the scaled offset expression calculated by the auxiliary functionresolveStructPtrVal()orresolvePtrVal()depending on the value of the memory itemm(basetype()is here an auxiliary function that maps the pointer selector to its base type, see Section 5.14). Furthermore, it is ensured that the index is within the bounds of the fitting memory itemm.

After the index and array expressions are constructed, the constraintc1is built, which requires that the validity constraints of all participating memory itemsm,mandmare valid. If this constraint is feasible, constraintc2 is built, which represents the resolution of the dereferenced pointer and requires that the index and array expressions are valid. This constraint is stored together with the corresponding pointer, feasibility constraint and the validity period in the resulting set of possible outcomes of the resolution process of the dereferenced pointerp. If it is detected that the memory itemmrefers to a simulated input variable, the input/output parameterisInputindicating whether the dereferenced pointer still points to an input is set totrue.

After we have defined how the resolution of the input array is handled, we are able to proceed with our example. We continue with step 2 of the resolution process:

2. The dereferenced pointer *D_176412 was already resolved to the base address &a@P[0] and offsetx0·4. The matching memory itemm2was found. Since the value ofm2is an array, the new part of the algorithm resolveDerefPtrExp()is now invoked. The index and array expressions are built:

idxExp=(idx012 == x00idx012·32 < 3200) arrayExp=(D_176515==rttArrayRead(a@P0, idx012)) The value of the indexidx012was scaled according to the size of array elements.

Now the following tuple is stored in the resolution setR:

(idxExp∧arrayExp,D_176412,true, 1).

Although the memory itemm2 refers to a simulated inputa@P, no further input pointers are de-tected and the resolution ofD_176515results in:

Φ= (D_176515>D_176815) (D_176515==rttArrayRead(a@P0, idx012)) (idx012 == x0 0idx012·32 < 3200).

3. ResolveD_176815: the resolution proceeds similar to the resolution ofD_176515and results in:

Φ= (D_176515>D_176815) (D_176515==rttArrayRead(a@P0, idx012)) (idx012 == x0 0idx012·32 < 3200)

(D_176815==rttArrayRead(a@P0, idx015)) (idx015 == y0 0idx015·32 < 3200).

4. No unresolved symbols exist anymore. Thus, the resolution process stops and the constructed path constraint is passed to the solver.

The solver determinesΦas feasible and returns the following solution (we omit here the listing of the calculated values for local and auxiliary variables, since they do not affect the generated test case):

a@P [ 0 ] = −513 a@P [ 1 ] = −1 x = 1 y = 0

Based on the calculated solution, the generator constructs the following test driver:

i n t a ;

u n s i g n e d i n t x ; u n s i g n e d i n t y ; i n t a _ r t t _ a r r a y [ 1 0 0 ] ; i n t _ _ r t t _ r e t u r n ;

@ r t t B e g i n T e s t S t e p; {

y = 0 ; x = 1 ;

a _ r t t _ a r r a y [ 0 ] = 513;

a = a _ r t t _ a r r a y ; a _ r t t _ a r r a y [ 1 ] = −1;

a = a _ r t t _ a r r a y ;

@ r t t C a l l( _ _ r t t _ r e t u r n = t e s t ( a , x , y ) ) ; }

@rttEndTestStep;

As was already mentioned, CTGEN generates tests in RT-Tester syntax [44]. To make settings in the input array, the auxiliary arraya_rtt_arrayis created. Its values are set according to the calculated by the solver and the input parameterais set to this array. The values ofxandyare set appropriately to the solution. These settings satisfy the guard condition(a[x] > a[y])and, consequently, this test will cover the intended branch. The complete test script as well as the other outputs produced by the generator for the discussed example can be observed in Appendix 12.