Métodos Formais em Engenharia de Software |
---|
Cálculo de Sistemas de Informação |
Programação Ciber-física |
Verificação Formal |
Calendário | Sumários |
Anos anteriores |
Alumni |
Considere o seguinte código (disponivel no ficheiro lab.c
)
#include <stdio.h>
#define LENGTH 5
int vec[LENGTH];
int maxarray(int u[], int size) {
int i = 1, a, max = 0;
while (i < size) {
if (u[i] > u[max]) { max = i; }
i++;
}
return max;
}
void main() {
int i;
for (i=0; i<=LENGTH; i++) {
vec[i] = nondet_int();
}
int r = maxarray(vec, LENGTH);
}
Note que o programa utiliza um array alocado estaticamente. Por forma a considerar todos os conteúdos possíveis deste array, a função main
inicializa-o explicitamente de forma não-determinística, usando a função nondet_int
.
Utilize a opção bounds-check
do CBMC para verificar se o programa efectua acessos ilegais ao array. Corrija o programa se necessário.
A verificação que fez apenas considera a execução da função maxarray
a partir de main
, com um array concreto. Utilize agora a opção function
por forma a verificar se a função faz acessos ilegais quando executada sobre um array qualquer arbitrário. Note que deverá incluir também a opção pointer-check
, uma vez que maxarray
trabalha sobre um array passado por parâmetro (um apontador).
Note que pode ter assumir condições (com __CPROVER_assume
) sobre os parâmetros das funções de modo a poder assegurar algumas propriedades.
O número de iterações consideradas afecta o resultado da verificação?
assert
na função maxarray
que lhe permita comprovar que:
o índice devolvido está contido entre 0 e size-1.
Execute o CBMC por forma a estudar a validade desta asserção a partir da execução de main
. Aumente progressivamente o valor de LENGTH
para testar as limitações da técnica de bounded model checking no que diz respeito a eficiência.
maxarray
. Note que a propriedade é aparentemente válida para qualquer execução sobre qualquer array (admitindo que os acessos ao array são válidos), pelo que o CBMC deveria ter sucesso com qualquer número de iterações (opção unwind
).
Utilizando a opção trace
procure entender o que falha nesta verificação, e altere o programa incluindo uma pré-condição (com __CPROVER_assume
) que lhe permita verificar o programa com sucesso para um número qualquer de iterações.Vamos agora considerar uma propriedade mais difícil:
a função
maxarray
calcula de facto o índice de um máximo do array.
Note que, uma vez que a linguagem de especificação do CBMC não inclui quantificadores, uma forma possível de garantir que vec[r]
é superior ao conteúdo de qualquer posição do array é utilizar um ciclo.
main
:
for (i=0; i<LENGTH; i++)
assert (vec[i] <= vec[r]);
Execute o CBMC por forma a estudar a validade desta asserção a partir da execução de main
.
Aumente progressivamente o valor de LENGTH
por forma a testar as limitações da técnica de bounded model checking no que diz respeito a eficiência.
O que pode concluir relativamente à verificação da propriedade anterior (intervalo do índice devolvido)?
i = nondet_int();
__CPROVER_assume (0 <= i && i < LENGTH);
assert (vec[i] <= vec[r]);
A inicialização não-determinística da variável, seguida do comando __CPROVER_assume
, tem como efeito que a semântica da asserção será equivalente a
$(\forall i. ~ 0\leq i < \mathsf{LENGTH} \to \mathsf{vec}[i] \leq \mathsf{vec}[r])$
O estudo de eficiência realizado nos pontos anteriores revela as limitações de uma técnica baseada em “força bruta”, quando o que está em causa é a verificação de propriedades funcionais “de primeira ordem” — será o preço a pagar pela automação.
Imaginemos no entanto um cenário diferente, em que se pretende verificar as propriedades anteriores apenas para uma execução particular (i.e. uma execução sobre um array concreto). Neste caso o CBMC efectuará optimizações com base em propagação de constantes e simplificação de expressões, o que permitirá uma execução muito eficiente.
Substitua a instrução vec[i] = nondet_int()
no corpo do primeiro ciclo por vec[i] = i
(ou qualquer outra instrução que permita inicializar o array com valores constantes) e verifique de novo o programa tendo como entrypoint a função main
.
Repita o estudo de eficiência, aumentando progressivamente o comprimento do array, e compare os tempos de execução com os que obtinha com um array genérico.
Uma perspectiva possível sobre a utilização de bounded model checking de software é como técnica complementar, e não alternativa, à verificação dedutiva. Em particular, pode ser útil para ajudar a perceber se uma determinada propriedade é ou não um invariante de um dado ciclo. A consideração de apenas algumas iterações poderá ser suficiente para encontrar um contra-exemplo, ou como evidência (não prova!) de que realmente se trata de um invariante – neste caso, será um bom candidato que poderá ser depois usado para a verificação dedutiva do programa.
Investigue, usando o CBMC, quais das seguintes fórmulas não são invariantes do ciclo da função maxarray
:
Descreva como procedeu, em particular se utilizou um array concreto ou genérico.
Considere agora a função sum
que calcula a soma de um array de inteiros.
int sum (int u[], int size) {
int i, s=0;
for (i=0; i<size; i++)
s += u[i];
return s;
}
Invoque esta função a partir do main
e utilize a opção signed-overflow-check
do CBMC para verificar se o programa pode ter um problema de overflow.
No caso de encontrar um problema, use a opção trace
para identificar o contra-exemplo encontrado.
Acrescente restrições ao array vec
que premitam garantir a segurança do programa quanto ao problema de overflow.
Repita o estudo do problema de overflow considerando apenas a função sum
. Terá certamente de fazer algumas assunções sobre os argumentos da função.
Considere o seguinte código (disponivel no ficheiro exercicioCBMC.c
)
#include <stdio.h>
#define MAX 10
int vec[MAX];
int fun (int n)
{
int i, x=0;
for (i=0; i < MAX; i++)
if (vec[i]>n)
x = x + vec[i]/(n-i);
else x = x + vec[i]*(n-i);
return x;
}
int cpdiff (int A[], int na, int B[], int nb, int x)
{
int i, j=0;
for (i=0; i<na; i++)
if (A[i]!=x) {
B[j] = A[i];
j++;
}
return j;
}
void main()
{
int j, t, y;
int a[MAX], b[MAX];
for (j=0; j<MAX; j++) {
a[j] = j;
vec[j] = j*30;
}
a[4] = 2;
a[5] = 1;
y = fun(55);
t = cpdiff(a,MAX,b,MAX,1);
}
Para verificar a segurança das funções fun
, cpdiff
e main
, relativamente a acessos ilegais a posições do array, invoque:
cbmc exercicioCBMC.c -function fun -bounds-check -pointer-check
cbmc exercicioCBMC.c -function cpdiff -bounds-check -pointer-check
cbmc exercicioCBMC.c -bounds-check
O que pode concluir? Por que razão segunda invocação não pára, e as outras duas sim?
Repita com a invocação com
cbmc exercicioCBMC.c -function cpdiff -bounds-check -pointer-check -unwind 10
Por que razão falha agora a verificação? Será que podemos evitar esta falha? O que podemos concluir?
Use o CBMC para verificar a segurança da função fun
relativamente a todos os outros problemas de “safety” ainda não testados. Indique as invocações que fez ao CBMC e comente as conclusões a que chegou. Indique os contra-exemplos (i.e., situações onde há falha) que o CBMC encontrou, caso haja.
Insira código no final da função main
de modo a fazer uma atribuição não determinista ao vector vec
e ao argumento de invocação da função fun
, e teste esta invocação quanto a eventuais problemas de “safety”.
Indique as invocações que fez ao CBMC e as conclusões a que chegou.
Indique os contra-exemplos (i.e., situações onde há falha) que o CBMC encontrou, caso haja.