diff --git a/src/plugins/instantiate/global_context.ml b/src/plugins/instantiate/global_context.ml index d28b409a52f87647dc991fddbd57582cf79928a2..9a6d4ff32d6ff6cf6e9e7c9b37db0e9bfb726fc7 100644 --- a/src/plugins/instantiate/global_context.ml +++ b/src/plugins/instantiate/global_context.ml @@ -21,19 +21,70 @@ (**************************************************************************) module Table = Datatype.String.Hashtbl -let table = Table.create 13 + +let varinfos = Table.create 13 +let logic_functions = Table.create 13 +let axiomatics = Table.create 13 let get_variable name make = - if Table.mem table name then Table.find table name + if Table.mem varinfos name then Table.find varinfos name else begin try Globals.Vars.find_from_astinfo name VGlobal with Not_found -> let vi = make () in - Table.add table name vi ; + Table.add varinfos name vi ; vi end -let clear () = Table.clear table +let get_logic_function name make = + if Table.mem logic_functions name then Table.find logic_functions name + else begin + match Logic_env.find_all_logic_functions name with + | [] -> + let li = make () in + Table.add logic_functions name li ; + Logic_utils.add_logic_function li ; + li + | [x] -> x + | _ :: _ -> Options.not_yet_implemented "Logic function overloading" + end + +let in_axiomatic_functions = Table.create 13 + +let get_logic_function_in_axiomatic name make = + if Table.mem in_axiomatic_functions name then + Table.find in_axiomatic_functions name + else begin + let make_then_find name = + let open Cil_types in + let (ax_name, ax_list), functions = make () in + List.iter + (fun f -> + Table.add in_axiomatic_functions f.l_var_info.lv_name f ; + Logic_utils.add_logic_function f) + functions ; + Table.add axiomatics ax_name ax_list ; + Table.find in_axiomatic_functions name + in + try match Logic_env.find_all_logic_functions name with + | [] -> make_then_find name + | [x] -> x + | _ :: _ -> Options.not_yet_implemented "Logic function overloading" + with Not_found -> + Options.fatal "Failed to build %s" name + end + +let clear () = Table.clear varinfos let globals loc = - Table.fold (fun _ x l -> Cil_types.GVarDecl(x, loc) :: l) table [] + let open Cil_types in + let l = [] in + let l = + Table.fold (fun _ x l -> GVarDecl(x, loc) :: l) varinfos l + in + let annot x loc = GAnnot(x, loc) in + let fun_or_pred x loc = annot (Dfun_or_pred (x, loc)) loc in + let axiomatic name list loc = annot(Daxiomatic(name, list, [], loc)) loc in + let l = Table.fold (fun _ x l -> fun_or_pred x loc :: l) logic_functions l in + let l = Table.fold (fun n x l -> axiomatic n x loc :: l) axiomatics l in + l \ No newline at end of file diff --git a/src/plugins/instantiate/global_context.mli b/src/plugins/instantiate/global_context.mli index 66e600361950f022101b39f1a09e106b89eab7a4..89f3a2124493da562f1fb01dd8e955b37b3f1f6a 100644 --- a/src/plugins/instantiate/global_context.mli +++ b/src/plugins/instantiate/global_context.mli @@ -22,8 +22,8 @@ open Cil_types -(** The purpose of this module is to create global variables when it is needed - by instantiation modules. +(** The purpose of this module global definitions when it is needed by + instantiation modules. *) (** [get_variable name f] searches for an existing variable [name]. If this @@ -34,6 +34,30 @@ open Cil_types *) val get_variable: string -> (unit -> varinfo) -> varinfo +(** [get_logic_function name f] searches for an existing logic function [name]. + If this function does not exists, it is created using [f]. If the logic + function must be part of an axiomatic block **DO NOT** use this function, + use [get_logic_function_in_axiomatic]. + + Note that function overloading is not supported. +*) +val get_logic_function: string -> (unit -> logic_info) -> logic_info + +(** [get_logic_function_in_axiomatic name f] searches for an existing logic + function [name]. If this function does not exists, an axiomatic definition + is created using [f]. + + [f] must return: + - the axiomatic in a form [name, list of the defintions (incl. functions)] + - all functions that are part of the axiomatic definition + + Note that function overloading is not supported. +*) +val get_logic_function_in_axiomatic: + string -> + (unit -> (string * global_annotation list) * logic_info list) -> + logic_info + (** Clears internal tables *) val clear: unit -> unit diff --git a/src/plugins/instantiate/stdlib/basic_alloc.ml b/src/plugins/instantiate/stdlib/basic_alloc.ml index ec54907d18f893e63fe77b54ebaa0d4dde15ecb5..3f9ced7c231c90ccc69f6782abddb0d012d47790 100644 --- a/src/plugins/instantiate/stdlib/basic_alloc.ml +++ b/src/plugins/instantiate/stdlib/basic_alloc.ml @@ -47,19 +47,6 @@ let valid_size ?loc typ size = in new_predicate { p with pred_name = ["correct_size"] } -let pis_allocable ?loc size = - let is_allocable = Logic_env.find_all_logic_functions "is_allocable" in - let is_allocable = as_singleton is_allocable in - papp ?loc (is_allocable, [here_label], [size]) - -let is_allocable ?loc size = - let p = pis_allocable ?loc size in - new_predicate { p with pred_name = [ "allocable" ]} - -let isnt_allocable ?loc size = - let p = pnot ?loc (pis_allocable ?loc size) in - new_predicate { p with pred_name = [ "allocable" ]} - let heap_status () = let name = "__fc_heap_status" in let make () = @@ -70,6 +57,55 @@ let heap_status () = let vi = Global_context.get_variable name make in Basic_blocks.cvar_to_tvar vi +let make_is_allocable () = + let name = "is_allocable" in + { + l_var_info = Cil_const.make_logic_var_global name (Ctype Cil.voidType) ; + l_type = None ; + l_tparams = []; + l_labels = [FormalLabel("L")]; + l_profile = [Cil_const.make_logic_var_formal "i" Linteger] ; + l_body = LBreads [new_identified_term (heap_status())]; + } + +let make_axiomatic_is_allocable loc () = + let is_allocable = make_is_allocable () in + let lv_i = Cil_const.make_logic_var_quant "i" Linteger in + let t_i = tvar lv_i in + let zero = tinteger 0 in + let max = + tinteger + (Integer.to_int + (Cil.max_unsigned_number (Cil.bitsSizeOf (size_t ())))) + in + let label = FormalLabel("L") in + let cond = pand (prel (Rlt, t_i, zero), prel (Rgt, t_i, max)) in + let app = pnot (papp (is_allocable,[label],[t_i])) in + let prop = pforall ([lv_i], pimplies (cond, app)) in + let gfun = Dfun_or_pred(is_allocable, loc) in + let axiom = Dlemma("never_allocable", true, [label],[],prop,[], loc) in + ("dynamic_allocation", [gfun ; axiom]), [is_allocable] + +let get_is_allocable loc = + Global_context.get_logic_function_in_axiomatic + "is_allocable" (make_axiomatic_is_allocable loc) + +let pis_allocable ?loc size = + let loc = match loc with + | None -> Cil_datatype.Location.unknown + | Some l -> l + in + let is_allocable = get_is_allocable loc in + papp ~loc (is_allocable, [here_label], [size]) + +let is_allocable ?loc size = + let p = pis_allocable ?loc size in + new_predicate { p with pred_name = [ "allocable" ]} + +let isnt_allocable ?loc size = + let p = pnot ?loc (pis_allocable ?loc size) in + new_predicate { p with pred_name = [ "allocable" ]} + let assigns_result ?loc typ deps = let heap_status = new_identified_term (heap_status ()) in let deps = match deps with diff --git a/src/plugins/instantiate/string/mem_utils.mli b/src/plugins/instantiate/string/mem_utils.mli index a6bbb923b7c7a39ff64c7ce5233de5abb414d4a1..2a580c48732ada81908001ff088e88858048da53 100644 --- a/src/plugins/instantiate/string/mem_utils.mli +++ b/src/plugins/instantiate/string/mem_utils.mli @@ -32,7 +32,7 @@ module type Function = sig val prototype: unit -> proto - (** receives the type of the lvalue and the types of the arguments recieved + (** receives the type of the lvalue and the types of the arguments received for a call to the function and returns [true] iff they are correct. The received types depend on the [prototype] of the module. - if the kind is [Data t] -> it is the exact type of the expr/lvalue diff --git a/src/plugins/instantiate/tests/stdlib/no_fc_stdlib.c b/src/plugins/instantiate/tests/stdlib/no_fc_stdlib.c new file mode 100644 index 0000000000000000000000000000000000000000..21498c2ef2b5ab29516316267bc33c5af6d46bbd --- /dev/null +++ b/src/plugins/instantiate/tests/stdlib/no_fc_stdlib.c @@ -0,0 +1,12 @@ +#include <stddef.h> + +void* malloc(size_t s); +void* calloc(size_t num, size_t size); +void free(void* ptr); + +void foo(void){ + int * p = malloc(sizeof(int)); + int * q = calloc(2, sizeof(int)); + free(p); + free(q); +} \ No newline at end of file diff --git a/src/plugins/instantiate/tests/stdlib/oracle/no_fc_stdlib.res.oracle b/src/plugins/instantiate/tests/stdlib/oracle/no_fc_stdlib.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..baa036c870fb4e23d59d27155db86156ea9f5bfb --- /dev/null +++ b/src/plugins/instantiate/tests/stdlib/oracle/no_fc_stdlib.res.oracle @@ -0,0 +1,253 @@ +[kernel] Parsing tests/stdlib/no_fc_stdlib.c (with preprocessing) +/* Generated by Frama-C */ +#include "stddef.h" +/*@ ghost extern int __fc_heap_status; */ + +/*@ +axiomatic dynamic_allocation { + predicate is_allocable{L}(ℤ i) + reads __fc_heap_status; + + axiom never_allocable{L}: + ∀ ℤ i; i < 0 ∧ i > 4294967295 ⇒ ¬is_allocable(i); + + } + +*/ +void *malloc(size_t s); + +void *calloc(size_t num, size_t size); + +void free(void *ptr); + +/*@ requires freeable: ptr ≡ \null ∨ \freeable(ptr); + assigns __fc_heap_status; + assigns __fc_heap_status \from __fc_heap_status, ptr; + frees ptr; + + behavior allocation: + assumes ptr ≢ \null; + ensures freed: \allocable(ptr); + assigns __fc_heap_status; + assigns __fc_heap_status \from __fc_heap_status, ptr; + frees ptr; + + behavior no_allocation: + assumes ptr ≡ \null; + assigns \nothing; + allocates \nothing; + + complete behaviors no_allocation, allocation; + disjoint behaviors no_allocation, allocation; + */ +void free_int(int *ptr) +{ + free((void *)ptr); + return; +} + +/*@ requires correct_size: 0 ≡ size % 4; + assigns \result, __fc_heap_status; + assigns \result \from __fc_heap_status, num, size; + assigns __fc_heap_status \from __fc_heap_status, num, size; + allocates \result; + + behavior allocation: + assumes allocable: is_allocable(num * size); + ensures fresh_result: \fresh{Old, Here}(\result,num * size); + ensures + zero_initialization: + ∀ ℤ j0; 0 ≤ j0 < num ⇒ *(\result + j0) ≡ 0; + ensures + initialization: + ∀ ℤ j0; 0 ≤ j0 < num ⇒ \initialized(\result + j0); + assigns \result, __fc_heap_status; + assigns \result \from __fc_heap_status, num, size; + assigns __fc_heap_status \from __fc_heap_status, num, size; + allocates \result; + + behavior no_allocation: + assumes allocable: ¬is_allocable(num * size); + ensures null_result: \result ≡ \null; + assigns \result; + assigns \result \from \nothing; + allocates \nothing; + + complete behaviors no_allocation, allocation; + disjoint behaviors no_allocation, allocation; + */ +int *calloc_int(size_t num, size_t size) +{ + int *__retres; + __retres = (int *)calloc(num,size); + return __retres; +} + +/*@ requires correct_size: 0 ≡ size % 4; + assigns \result, __fc_heap_status; + assigns \result \from __fc_heap_status, size; + assigns __fc_heap_status \from __fc_heap_status, size; + allocates \result; + + behavior allocation: + assumes allocable: is_allocable(size); + ensures fresh_result: \fresh{Old, Here}(\result,size); + assigns \result, __fc_heap_status; + assigns \result \from __fc_heap_status, size; + assigns __fc_heap_status \from __fc_heap_status, size; + allocates \result; + + behavior no_allocation: + assumes allocable: ¬is_allocable(size); + ensures null_result: \result ≡ \null; + assigns \result; + assigns \result \from \nothing; + allocates \nothing; + + complete behaviors no_allocation, allocation; + disjoint behaviors no_allocation, allocation; + */ +int *malloc_int(size_t size) +{ + int *__retres; + __retres = (int *)malloc(size); + return __retres; +} + +void foo(void) +{ + int *p = malloc_int(sizeof(int)); + int *q = calloc_int((unsigned int)2,sizeof(int)); + free_int(p); + free_int(q); + return; +} + + +[kernel] Parsing tests/stdlib/result/no_fc_stdlib.c (with preprocessing) +/* Generated by Frama-C */ +#include "stddef.h" +/*@ ghost extern int __fc_heap_status; */ + +/*@ +axiomatic dynamic_allocation { + predicate is_allocable{L}(ℤ i) + reads __fc_heap_status; + + axiom never_allocable{L}: + ∀ ℤ i; i < 0 ∧ i > 4294967295 ⇒ ¬is_allocable(i); + + } + +*/ +void *malloc(size_t s); + +void *calloc(size_t num, size_t size); + +void free(void *ptr); + +/*@ requires freeable: ptr ≡ \null ∨ \freeable(ptr); + assigns __fc_heap_status; + assigns __fc_heap_status \from __fc_heap_status, ptr; + frees ptr; + + behavior allocation: + assumes ptr ≢ \null; + ensures freed: \allocable(\old(ptr)); + assigns __fc_heap_status; + assigns __fc_heap_status \from __fc_heap_status, ptr; + frees ptr; + + behavior no_allocation: + assumes ptr ≡ \null; + assigns \nothing; + allocates \nothing; + + complete behaviors no_allocation, allocation; + disjoint behaviors no_allocation, allocation; + */ +void free_int(int *ptr) +{ + free((void *)ptr); + return; +} + +/*@ requires correct_size: 0 ≡ size % 4; + assigns \result, __fc_heap_status; + assigns \result \from __fc_heap_status, num, size; + assigns __fc_heap_status \from __fc_heap_status, num, size; + allocates \result; + + behavior allocation: + assumes allocable: is_allocable(num * size); + ensures + fresh_result: \fresh{Old, Here}(\result,\old(num) * \old(size)); + ensures + zero_initialization: + ∀ ℤ j0; 0 ≤ j0 < \old(num) ⇒ *(\result + j0) ≡ 0; + ensures + initialization: + ∀ ℤ j0; 0 ≤ j0 < \old(num) ⇒ \initialized(\result + j0); + assigns \result, __fc_heap_status; + assigns \result \from __fc_heap_status, num, size; + assigns __fc_heap_status \from __fc_heap_status, num, size; + allocates \result; + + behavior no_allocation: + assumes allocable: ¬is_allocable(num * size); + ensures null_result: \result ≡ \null; + assigns \result; + assigns \result \from \nothing; + allocates \nothing; + + complete behaviors no_allocation, allocation; + disjoint behaviors no_allocation, allocation; + */ +int *calloc_int(size_t num, size_t size) +{ + int *__retres; + __retres = (int *)calloc(num,size); + return __retres; +} + +/*@ requires correct_size: 0 ≡ size % 4; + assigns \result, __fc_heap_status; + assigns \result \from __fc_heap_status, size; + assigns __fc_heap_status \from __fc_heap_status, size; + allocates \result; + + behavior allocation: + assumes allocable: is_allocable(size); + ensures fresh_result: \fresh{Old, Here}(\result,\old(size)); + assigns \result, __fc_heap_status; + assigns \result \from __fc_heap_status, size; + assigns __fc_heap_status \from __fc_heap_status, size; + allocates \result; + + behavior no_allocation: + assumes allocable: ¬is_allocable(size); + ensures null_result: \result ≡ \null; + assigns \result; + assigns \result \from \nothing; + allocates \nothing; + + complete behaviors no_allocation, allocation; + disjoint behaviors no_allocation, allocation; + */ +int *malloc_int(size_t size) +{ + int *__retres; + __retres = (int *)malloc(size); + return __retres; +} + +void foo(void) +{ + int *p = malloc_int(sizeof(int)); + int *q = calloc_int((unsigned int)2,sizeof(int)); + free_int(p); + free_int(q); + return; +} + +