1

I have read these two posts: Getting result of value analysis and Getting the values of statement. These two posts provide invaluable information on how to print the values of the value analysis.

However, my task requires me to extract the integers stored in the value variable, and then do some math with the integers (I am only concerned with integer values). For example, if the value analysis result for some variable is {1, 2}, I want to get the result as an OCaml list of integers: [1, 2]. This way I can do math with it. If the result involves an interval, I assume I can define a type to handle it. For example,

type point_or_interval =
| Point of int
| Interval of int * int

The type of the value variable is defined as type t = Cvalue.V.t in the documentation. I have not been able to find this module in the source, so I do not know how to manipulate the value and extract the information that I need. How should I do this? A code illustration will be appreciated!

Edit: I have tried the following code. This code is copied verbatim from Getting result of value analysis, with only some modifications in the pretty_vi function. It is not working with my test input program - Locations.Location_Bytes.find_lonely_key function raises Not_found exception. My input program is also attached.

open Cil_types

(* Prints the value associated to variable [vi] before [stmt]. *)
let pretty_vi fmt stmt vi =
  let kinstr = Kstmt stmt in (* make a kinstr from a stmt *)
  let lval = (Var vi, NoOffset) in (* make an lval from a varinfo *)
  let loc = (* make a location from a kinstr + an lval *)
    !Db.Value.lval_to_loc kinstr ~with_alarms:CilE.warn_none_mode lval
  in    
  Db.Value.fold_state_callstack
    (fun state () ->
       (* for each state in the callstack *)
       let value = Db.Value.find state loc in (* obtain value for location *)
       let base, offset = Locations.Location_Bytes.find_lonely_key value in 
       (match offset with 
       | Ival.Set _ -> ()
       | Ival.Float _ -> ()
       | Ival.Top (_, _, _, _ )-> ());
       Format.fprintf fmt "%a -> %a@." Printer.pp_varinfo vi
         Db.Value.pretty value (* print mapping *)
    ) () ~after:false kinstr

(* Prints the state at statement [stmt] for each local variable in [kf],
   and for each global variable. *)
let pretty_local_and_global_vars kf fmt stmt =
  let locals = Kernel_function.get_locals kf in
  List.iter (fun vi -> pretty_vi fmt stmt vi) locals (*;
  Globals.Vars.iter (fun vi _ -> pretty_vi fmt stmt vi) *)

(* Visits each statement in [kf] and prints the result of Value before the
   statement. *)
class stmt_val_visitor kf =
  object (self)
    inherit Visitor.frama_c_inplace
    method! vstmt_aux stmt =
      (match stmt.skind with
       | Instr _ ->
         Format.printf "state for all variables before stmt: %a@.%a@."
           Printer.pp_stmt stmt (pretty_local_and_global_vars kf) stmt
       | _ -> ());
      Cil.DoChildren
  end

(* usage: frama-c file.c -load-script print_vals.ml *)
let () =
  Db.Main.extend (fun () ->
      Format.printf "computing value...@.";
      !Db.Value.compute ();
      let fun_name = "main" in
      Format.printf "visiting function: %s@." fun_name;
      let kf_vis = new stmt_val_visitor in
      let kf = Globals.Functions.find_by_name fun_name in
      let fundec = Kernel_function.get_definition kf in
      ignore (Visitor.visitFramacFunction (kf_vis kf) fundec);
      Format.printf "done!@.")

Test input program:

#include <stdio.h>

int main() {
    int a = 1;
    return 0;
}

What is the problem with this code? Why is the mapping of the value not found?

Community
  • 1
  • 1
Seves
  • 135
  • 2
  • 12

1 Answers1

3

General remark: if you are using an editor which supports Merlin, I seriously recommend using it. It makes it easier to find in which module things are defined, which types are synonyms, and, combined with an auto-completion tool, Merlin allows you to find conversion functions much more easily.

In particular, Merlin should help you find out that Cvalue.V.project_ival : V.t -> Ival.t converts a V.t into an Ival.t (assuming the value is convertible, e.g. it is not a pointer).

Ival.t is a sophisticated interval-like value that can represent:

  • a contiguous interval of floating-point values (Ival.Float);
  • a small set of integer values (Ival.Set);
  • or an actual integer interval (Ival.Top, despite the name), with congruence information and optional bounds, e.g. [9..--]1%4 represents {x ∈ ℕ | x ≥ 9 ∧ x mod 4 = 1}.

Function Ival.min_and_max : Ival.t -> Integer.t option * Integer.t option takes an Ival.t and returns (assuming the interval does not contain a floating-point interval) a pair (maybe_min, maybe_max), where maybe_min is None if there is no lower bound (minus infinity), or Some min otherwise, and symmetrically for maybe max. It works both with Ival.Set and Ival.Top.

Note that Integer.t are not machine integers, but an implementation of arbitrary-precision integers.

anol
  • 8,264
  • 3
  • 34
  • 78
  • Thanks! @anol This is very understandable! I have written some code, but it is not working with my input program. Do you know what is wrong with my code? I have edited my question with the code attached. – Seves Apr 07 '16 at 03:21
  • My code was more of a quick, "hackish" solution to give the general idea than an actual bullet-proof code. In particular, whenever the doc says something like `raise @Not_found otherwise` (as is the case for `find_lonely_key`, you **must** do a `try .. with`, and either print a decent exception, or deal with it properly. For instance, you should test whether the value obtained by the state is empty (bottom), in which case you should print it as empty. – anol Apr 07 '16 at 06:53
  • By adding enough pretty-printing (e.g. catching `Not_found` and printing the current statement and varinfo), you'll find out that either variable `a` *before* the first statement is empty, or that variable `__retres` (in the normalized code created by CIL) is empty *after* the first statement, but before the second one. So properly dealing with an empty state should solve both cases. But then you have to handle the case where there are multiple bases as well, etc. – anol Apr 07 '16 at 06:53