Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • yassine.ettayeb/anastat-frama-c-24-25
  • nicolas.descamps/anastat
  • rayan.lachguel/anastat-frama-c-24-25
  • merlin.baviere/ana-stat-frama-c-24-25-merlin-baviere
  • thomas.norodom/norodom-ana-stat-frama-c-24-25
  • quentin.vicens/ana-stat-frama-c-24-25
  • stanislas.plowiecki/anastat-frama-c-24-25
  • virgile.prevosto/anastat-frama-c-24-25
8 results
Show changes
Commits on Source (8)
Showing
with 507 additions and 0 deletions
/config/desktop-background.png
/config/ssl
/config/.*
*/config/desktop-background.png
*/config/ssl
*/config/.*
Ce répertoire contient les transparents des cours passés,
ainsi que les exemples Frama-C/Eva présentés dans le cours 1,
dans le sous-répertoire config, ce qui permet de les avoir
automatiquement dans l'image Docker du cours
(voir https://github.com/Frederic-Boulanger-UPS/docker-webtop-3asl
pour plus d'information).
Chaque exemple contient dans un commentaire la ligne
de commande Frama-C qui permet de faire l'analyse avec
le niveau de précision voulue.
/* frama-c -eva -main f 01-numeric.c */
int x, y, t, m;
double d;
extern char z;
char z1;
void f(int c) {
if (c) x = 40;
for (int i = 0; i <= 40; i++) {
Frama_C_show_each_loop(i,y);
if (c == i) y = i;
}
z1 = z;
t = z;
m = 3;
for (int i = 3; i <= 40; i += 4) {
if (c == i) m = i;
}
if (c) {
d = 0.25;
} else {
d = 3.125;
}
}
/* frama-c -eva -main f 02-address.c */
#include <stdlib.h>
extern int* a;
int *x;
const char *y;
int *z;
int *t;
int *u;
int p[3];
const char *string = "foobar";
void f(int c) {
if (c) {
x = &p[1];
} else {
x = &p[2];
}
y = string;
z = (int *)1024;
t = (int *)((unsigned long)a | 4096);
u = malloc(sizeof(*u));
}
/* frama-c -eva 03-address_written.c */
int X, Y;
int *p;
void f(int c) {
int x, y;
if (c <= 0) x = 2;
L:
if (c <= 0) y = x + 1;
p = c ? &X : &x;
}
int main(int c) {
f(c);
}
/* frama-c -eva -main f 04-cast.c */
typedef struct {
int *monpointeur;
short monshort;
} t;
void f(int c) {
int x = 3;
if (c) x = 24;
t S = {&x, 12};
char *p = (char *)&S;
p[2] = 4;
}
/* frama-c -eva 05-domains.c
frama-c -eva -eva-domains symbolic-locations,octagon 05-domains.c
*/
#include "__fc_builtin.h"
int main () {
int x, y;
int *p = Frama_C_nondet_ptr(&x,&y);
*p = 3;
int z = (*p)++;
y = Frama_C_interval(0,10);
x = y;
z = x - y;
}
/* frama-c -eva 06-simple.c */
int S = 0;
int T[5];
int main(void) {
int i;
int *p = &T[0];
/*@ loop unroll 6; */
for (i = 0; i < 5; i++) {
S = S + i;
*p++ = S;
}
return S;
}
/* frama-c -eva -eva-slevel 3 07-slevel.c */
int x, y;
void main(int c) {
//@ assert c<=0 || c > 0;
if (c) {
x = 10;
} else {
x = 33;
}
Frama_C_show_each(c);
if (!c) {
x++;
} else {
x--;
}
if (c <= 0) {
y = 42;
} else {
y = 36;
}
if (c > 0) {
y++;
} else {
y--;
}
}
/* frama-c -eva -eva-slevel 2 -eva-split-return-function f:0 08-split.c */
extern int c;
int f(int *p) {
//@ split c <= 0;
if (c <= 0) {
*p = 10;
return 0;
}
return -1;
}
int main() {
int x;
int res = f(&x);
if (!res)
x++;
else
x = 0;
}
/* frama-c -eva -eva-context-width 4 -main search 09-context.c */
int search(char *a, char key) {
char *orig = a;
while (*a) {
if (*a == key) return a - orig;
a++;
}
return -1;
}
/* frama-c -eva -eva-precision 3 09-context.c 10-context-driver.c -main driver */
#include "__fc_builtin.h"
#include "limits.h"
int search(char *a, char key);
extern char buffer[1024];
int driver() {
buffer[1023] = 0;
char key = Frama_C_interval(CHAR_MIN, CHAR_MAX);
return search(buffer, key);
}
File added
File added
File added
"""Control flow graphs
- Label and its subclasses describe the label on the edges of the CfGs
- Nodes describes nodes, with an id and its successors
- Graph describes a graph
- Cfg describes a cfg, i.e. a graph with initial, final, and error nodes
- function cfg creates a cfg for the given program
"""
from syntax import *
from dataclasses import dataclass
@dataclass
class Label:
"""generic class for edges' labels"""
pass
@dataclass
class LSkip(Label):
"""label skip (does nothing)"""
def __str__(self):
return "skip"
@dataclass
class LSet(Label):
"""assignment label"""
var: str
expr: ArithExpr
def __str__(self):
return f"{self.var} := {self.expr}"
@dataclass
class LTest(Label):
"""test label"""
cond: BoolExpr
def __str__(self):
return f"?{self.cond}"
@dataclass
class LErr(Label):
"""error label"""
err: Expr
def __str__(self):
return f"Err({self.err})"
class Node:
"""node of the graph, with its id and its successors"""
def __init__(self,nid):
self.nid = nid
self.succs = []
def __str__(self):
return f"Node {self.nid}"
class Graph:
nb_id: int
nodes: list[Node]
"""a graph, i.e. a list of nodes"""
def __init__(self):
self.nb_nid = 0
self.nodes = []
def new_node(self) -> Node:
"""creates a new node, with a fresh id and no succs"""
node = Node(self.nb_nid)
self.nb_nid += 1
self.nodes += [node]
return node
def new_edge(self,n1: Node,n2: Node,l: Label) -> None:
"""creates a new edge.
If the nodes are not already in the graph, they are added.
In that case, no check is made that a node with the same id
is already present or not.
"""
if n1 not in self.nodes:
self.nodes += [n1]
if n2 not in self.nodes:
self.nodes += [n2]
n1.succs += [(n2,l)]
def cfg_aux(g:Graph,e:Node,i:Node,instr:Instr) -> Node:
"""auxiliary function to build a CfG
takes as argument a graph, the error node, the node starting
the current instruction, and the instruction itself, returns
the final node of current instruction
"""
match instr:
case ISkip():
f = g.new_node()
g.new_edge(i,f,LSkip())
return f
case ISet(var,expr):
f = g.new_node()
g.new_edge(i,f,LSet(var,expr))
g.new_edge(i,e,LErr(expr))
return f
case ISeq(first,second):
m = cfg_aux(g,e,i,first)
return cfg_aux(g,e,m,second)
case ICond(cond,true_branch,false_branch):
t = g.new_node()
f = g.new_node()
g.new_edge(i,t,LTest(cond))
g.new_edge(i,f,LTest(BENeg(cond)))
g.new_edge(i,e,LErr(cond))
tf = cfg_aux(g,e,t,true_branch)
ff = cfg_aux(g,e,f,false_branch)
f = g.new_node()
g.new_edge(tf,f,LSkip())
g.new_edge(ff,f,LSkip())
return f
case ILoop(cond,body):
ib = g.new_node()
f = g.new_node()
g.new_edge(i,ib,LTest(cond))
g.new_edge(i,f,LTest(BENeg(cond)))
g.new_edge(i,e,LErr(cond))
fb = cfg_aux(g,e,ib,body)
g.new_edge(fb,i,LSkip())
return f
case _: assert False
class Cfg:
"""describes a CfG"""
g: Graph
err_node: Node
init_node: Node
final_node: Node
def __init__(self,instr):
self.g = Graph()
self.err_node = self.g.new_node()
self.init_node = self.g.new_node()
self.final_node = cfg_aux(self.g,self.err_node,self.init_node,instr)
def __str__(self):
res = ""
for node in self.g.nodes:
res+=f"{node}:\n"
for (dest,lab) in node.succs:
res+=f" {lab} --> {dest}\n"
res+=f"init node is {self.init_node}\n"
res+=f"final node is {self.final_node}\n"
res+=f"err node is {self.err_node}\n"
return res
"""
implements a constant propagation analysis
"""
from dataclasses import dataclass
from cfg import Label, LSkip, LSet, LTest, LErr
from iteration import Transfer
from syntax import *
@dataclass
class abstract_value[T]:
"""None indicates that we don't know the precise value"""
value: T | None
def __str__(self):
return f"{self.value}"
type abstract_env = dict[str, abstract_value[int]]
"""mapping from variables to abstract values.
As with concrete environment, a variable not in the dictionary will
lead to an error if we try to obtain its value
"""
type state = abstract_env | None
"""abstract state is either an abstract env or bottom
"""
def eval_aexp(env: abstract_env, e: ArithExpr) -> abstract_value[int] | None:
"""evaluate an arithmetic expression in an abstract environment
returns None in case of error
"""
match e:
case AECst(value): return abstract_value(value)
case AEVar(var):
if var in env: return env[var]
else: return None
case AEUop(uop,expr):
res = eval_aexp(env,expr)
if res is None: return None
if res.value is None: return abstract_value(None)
if uop == Uop.OPP: return abstract_value(-res.value)
return None
case AEBop(bop,left_expr,right_expr):
v1 = eval_aexp(env,left_expr)
v2 = eval_aexp(env,right_expr)
if v1 is None or v2 is None: return None
if v1.value is None or v2.value is None:
return abstract_value(None)
match bop:
case Bop.ADD: return abstract_value(v1.value+v2.value)
case Bop.MUL: return abstract_value(v1.value*v2.value)
case Bop.DIV:
if v2.value == 0: return None
return abstract_value(v1.value//v2.value)
case _: assert False
def eval_bexp(env: abstract_env, e: BoolExpr) -> abstract_value[bool] | None:
"""abstract evaluation of a boolean expression"""
match e:
case BEPlain(aexpr):
res = eval_aexp(env, aexpr)
if res is None: return None
if res.value is None: return abstract_value(None)
return abstract_value(res.value!=0)
case BEEq(left_expr,right_expr):
v1 = eval_aexp(env, left_expr)
v2 = eval_aexp(env, right_expr)
if v1 is None or v2 is None: return None
if v1.value is None or v2.value is None: return abstract_value(None)
return abstract_value(v1.value == v2.value)
case BELeq(left_expr,right_expr):
v1 = eval_aexp(env, left_expr)
v2 = eval_aexp(env, right_expr)
if v1 is None or v2 is None: return None
if v1.value is None or v2.value is None: return abstract_value(None)
return abstract_value(v1.value <= v2.value)
case BENeg(expr):
v = eval_bexp(env,expr)
if v is None: return None
if v.value is None: return v
return abstract_value(not v.value)
case _: assert False
class Constant_propagation(Transfer[state]):
variables: frozenset[str]
def __init__(self,instr):
self.variables = variables_of_instr(instr)
def bottom(self) -> state:
...
def init_state(self) -> state:
...
def join(self,s1:state,s2:state) -> state:
...
def included(self,s1: state,s2: state) -> bool:
...
def tr_skip(self,s: state) -> state:
...
def tr_set(self,s: state,v: str,e: ArithExpr) -> state:
...
def tr_test(self,s: state,c: BoolExpr) -> state:
...
def tr_err(self,s: state,e: Expr) -> state:
...
from syntax import *
from cfg import Cfg
from iteration import fixpoint_iteration
from constant_propagation import Constant_propagation
x = AEVar("x")
y = AEVar("y")
c = AEVar("c")
one = AECst(1)
zero = AECst(0)
propagate_compute=ISeq(ISet("x",one),ISet("x", AEBop(Bop.ADD,x,one)))
same_branch_if=ICond(BELeq(c,zero), ISet("y", one), ISet("y",one))
equal_branch=ICond(BEEq(c,one),ISkip(),ISet("c",one))
prog = ISeq(ISeq(propagate_compute,same_branch_if),equal_branch)
cfg = Cfg(prog)
cst_propagation = Constant_propagation(prog)
res = fixpoint_iteration(cst_propagation,cfg)
for (node, state) in res.items():
print(f"{node}:")
if state is None: print(" None")
else:
for (var, value) in state.items():
print(f" {var} -> {value}")
from interval_analysis import analyze
from example_opsem import factorial
analyze(factorial)
from syntax import *
from interval_analysis import eval_aexp, eval_bexp, abstract_env, Interval
x: ArithExpr = AEVar("x")
y: ArithExpr = AEVar("y")
# x - y * (x / y)
test_aexp: ArithExpr = AEBop(Bop.ADD,x, AEUop(Uop.OPP,AEBop(Bop.MUL,y,AEBop(Bop.DIV,x,y))))
# ! (x - y <= y * (x - x/y))
test_bexp: BoolExpr = BENeg(BELeq(AEBop(Bop.ADD,x,AEUop(Uop.OPP,y)),test_aexp))
def test(env: abstract_env) -> None:
res_a = eval_aexp(env,test_aexp)
res_b = eval_bexp(env,test_bexp)
print(f'In environment { {var: str(i) for (var,i) in env.items()} }')
print(f"{test_aexp} -> {res_a}")
print(f"{test_bexp} -> {res_b}")
test({ 'x': Interval(20,20), 'y': Interval(2,2) })
test({ 'x': Interval(10,20), 'y': Interval(1,2) })
test({ 'x': Interval(-30,0), 'y': Interval(-10,0) })
test({ 'x': Interval(-20,20), 'y': Interval(0,0) })
test({ 'x': Interval(0,20), 'y': Interval(2,5) })
test({ 'x': Interval(-10,20), 'y': Interval(-5,-2) })
test({})