package frama-c
Platform dedicated to the analysis of source code written in C
Install
Dune Dependency
Authors
-
MMichele Alberti
-
TThibaud Antignac
-
GGergö Barany
-
PPatrick Baudin
-
NNicolas Bellec
-
TThibaut Benjamin
-
AAllan Blanchard
-
LLionel Blatter
-
FFrançois Bobot
-
RRichard Bonichon
-
VVincent Botbol
-
QQuentin Bouillaguet
-
DDavid Bühler
-
ZZakaria Chihani
-
SSylvain Chiron
-
LLoïc Correnson
-
JJulien Crétin
-
PPascal Cuoq
-
ZZaynah Dargaye
-
BBasile Desloges
-
JJean-Christophe Filliâtre
-
PPhilippe Herrmann
-
MMaxime Jacquemin
-
BBenjamin Jorge
-
FFlorent Kirchner
-
AAlexander Kogtenkov
-
RRemi Lazarini
-
TTristan Le Gall
-
KKilyan Le Gallic
-
JJean-Christophe Léchenet
-
MMatthieu Lemerre
-
DDara Ly
-
DDavid Maison
-
CClaude Marché
-
AAndré Maroneze
-
TThibault Martin
-
FFonenantsoa Maurica
-
MMelody Méaulle
-
BBenjamin Monate
-
YYannick Moy
-
PPierre Nigron
-
AAnne Pacalet
-
VValentin Perrelle
-
GGuillaume Petiot
-
DDario Pinto
-
VVirgile Prevosto
-
AArmand Puccetti
-
FFélix Ridoux
-
VVirgile Robles
-
JJan Rochel
-
MMuriel Roger
-
CCécile Ruet-Cros
-
JJulien Signoles
-
NNicolas Stouls
-
KKostyantyn Vorobyov
-
BBoris Yakobowski
Maintainers
Sources
frama-c-31.0-Gallium.tar.gz
sha256=a94384f00d53791cbb4b4d83ab41607bc71962d42461f02d71116c4ff6dca567
doc/src/frama-c-pdg.core/build.ml.html
Source file build.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
(**************************************************************************) (* *) (* This file is part of Frama-C. *) (* *) (* Copyright (C) 2007-2025 *) (* CEA (Commissariat à l'énergie atomique et aux énergies *) (* alternatives) *) (* *) (* you can redistribute it and/or modify it under the terms of the GNU *) (* Lesser General Public License as published by the Free Software *) (* Foundation, version 2.1. *) (* *) (* It is distributed in the hope that it will be useful, *) (* but WITHOUT ANY WARRANTY; without even the implied warranty of *) (* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) (* GNU Lesser General Public License for more details. *) (* *) (* See the GNU Lesser General Public License version 2.1 *) (* for more details (enclosed in the file licenses/LGPLv2.1). *) (* *) (**************************************************************************) (** Build graphs (PDG) for the function (see module {!module: Build.BuildPdg}) to represent the dependencies between instructions in order to use it for slicing purposes. A function is processed using a forward dataflow analysis (see module {{: ../html/Dataflow2.html}Dataflow2} which is instantiated with the module {!module: Build.Computer} below). *) open Pdg_types let dkey = Pdg_parameters.register_category "build" let debug fmt = Pdg_parameters.debug ~dkey fmt let debug2 fmt = Pdg_parameters.debug ~dkey fmt ~level:2 open Cil_types open Cil_datatype open PdgTypes open PdgIndex (* exception Err_Top of string *) exception Err_Bot of string (** set of nodes of the graph *) module BoolNodeSet = Stdlib.Set.Make(Datatype.Pair(Datatype.Bool)(PdgTypes.Node)) let pretty_node ?(key=false) fmt n = PdgTypes.Node.pretty fmt n; if key then Format.fprintf fmt ": %a" PdgIndex.Key.pretty (PdgTypes.Node.elem_key n) let is_variadic kf = let varf = Kernel_function.get_vi kf in match varf.vtype.tnode with | TFun (_, _, is_variadic) -> is_variadic | _ -> Pdg_parameters.fatal "The variable of a kernel_function has to be a function !" (* -------------------------------------------------------------------------- *) (* --- Auxiliary functions --- *) (* -------------------------------------------------------------------------- *) type arg_nodes = Node.t list (** type of the whole PDG representation during its building process *) type pdg_build = { fct : kernel_function; mutable other_inputs : (PdgTypes.Node.t * Dpd.td * Locations.Zone.t) list; graph : G.t; states : Pdg_state.states; index : PdgTypes.Pdg.fi; ctrl_dpds : BoolNodeSet.t Stmt.Hashtbl.t ; (** The nodes to which each stmt control-depend on. * The links will be added in the graph at the end. *) decl_nodes : Node.t Varinfo.Hashtbl.t ; (** map between declaration nodes and the variables to build the dependencies. *) } (** create an empty build pdg for the function*) let create_pdg_build kf = let nb_stmts = if Eva.Analysis.use_spec_instead_of_definition kf then 17 else List.length (Kernel_function.get_definition kf).sallstmts in let index = FctIndex.create nb_stmts in let states = Stmt.Hashtbl.create nb_stmts in let graph = G.create () in { fct = kf; graph = graph; states = states; index = index; other_inputs = []; ctrl_dpds = Stmt.Hashtbl.create nb_stmts ; decl_nodes = Varinfo.Hashtbl.create 10 ; } let _pretty fmt pdg = PdgTypes.Pdg.pretty_graph fmt pdg.graph (** add a node to the PDG, but if it is associated with a stmt, check before if it doesn't exist already (useful for loops). @return the (new or old) node. *) let add_elem pdg key = match key with | Key.CallStmt _ -> assert false | _ -> try FctIndex.find_info pdg.index key with Not_found -> let new_node = G.add_elem pdg.graph key in debug "add_new_node %a@." (pretty_node ~key:true) new_node; FctIndex.add pdg.index key new_node; new_node let decl_var pdg var = let new_node = add_elem pdg (Key.decl_var_key var) in Varinfo.Hashtbl.add pdg.decl_nodes var new_node; new_node let get_var_base zone = try let base, _ = Locations.Zone.find_lonely_key zone in match base with | Base.Var (var,_) -> Some var | _ -> None with Not_found -> None (** add a dependency with the given label between the two nodes. Pre : the nodes have to be already in pdg. *) let add_dpd_in_g graph v1 dpd_kind part_opt v2 = debug "add_dpd : %a -%a-> %a@." PdgTypes.Node.pretty v1 Dpd.pretty_td dpd_kind PdgTypes.Node.pretty v2; G.add_dpd graph v1 dpd_kind part_opt v2 let add_z_dpd pdg n1 k z_part n2 = add_dpd_in_g pdg.graph n1 k z_part n2 let add_ctrl_dpd pdg n1 n2 = add_dpd_in_g pdg.graph n1 Dpd.Ctrl None n2 let add_decl_dpd pdg n1 k n2 = add_dpd_in_g pdg.graph n1 k None n2 (** add a dependency on the variable declaration. The kind of the dependency is address if the variable appears in a lvalue, data otherwise. *) let add_decl_dpds pdg node dpd_kind varset = let add_dpd var = try let var_decl_node = Varinfo.Hashtbl.find pdg.decl_nodes var in add_decl_dpd pdg node dpd_kind var_decl_node with Not_found -> () in Varinfo.Set.iter add_dpd varset (** [add_dpds pdg v dpd_kind state loc] * add 'dpd_kind' dependencies from node n to each element * which are stored for loc in state *) let add_dpds pdg n dpd_kind state loc = let add (node,z_part) = (* we only use [z_part] for dependencies to OutCall. * Would it be interesting to have it on other cases ? *) let z_part = match PdgTypes.Node.elem_key node with | PdgIndex.Key.SigCallKey (_, PdgIndex.Signature.Out (PdgIndex.Signature.OutLoc _)) -> z_part | _ -> None in add_z_dpd pdg n dpd_kind z_part node in let nodes, undef_zone = Pdg_state.get_loc_nodes state loc in List.iter add nodes; match undef_zone with | None -> () | Some undef_zone -> pdg.other_inputs <- (n, dpd_kind, undef_zone) :: pdg.other_inputs (** Process and clear [pdg.ctrl_dpds] which contains a mapping between the * statements and the control dependencies that have to be added to the * statement nodes. * Because some jump nodes can vanish due to optimisations using the value * analysis, we can not rely on the transitivity of the dependencies. * So let's compute a transitive closure of the control dependencies. * The table gives : stmt -> ctrl dependency nodes of the statement. * So for each stmt, we have to find if some of its ctrl nodes * also have dependencies that have to be added to the stmt. * *) let add_ctrl_dpds pdg = let add_indirect ctrl_node_set = (* Also add the ctrl_node dependencies to the set. * TODOopt: probably a better way to do that if it happens to work ! *) let rec add_node (real, n) (acc, seen) = if BoolNodeSet.mem (real, n) seen then (acc, seen) else let seen = BoolNodeSet.add (real, n) seen in let acc = if real then BoolNodeSet.add (true, n) acc else acc in add_rec n (acc, seen) and add_rec ctrl_node acc = match PdgTypes.Node.elem_key ctrl_node with | Key.Stmt ctrl_stmt -> (try let stmt_dpds = Stmt.Hashtbl.find pdg.ctrl_dpds ctrl_stmt in BoolNodeSet.fold add_node stmt_dpds acc with Not_found -> acc) | _ -> (* strange control dependency ! Ignore. *) acc in let acc = BoolNodeSet.empty, BoolNodeSet.empty in let acc, _ = BoolNodeSet.fold add_node ctrl_node_set acc in acc in let add_stmt_ctrl_dpd stmt ctrl_node_set = let stmt_nodes = try FctIndex.find_all pdg.index (Key.stmt_key stmt) with Not_found -> [] (* some stmts have no node if they are dead code for instance*) in let label_nodes acc label = try acc @ FctIndex.find_all pdg.index (Key.label_key stmt label) with Not_found -> acc in let stmt_nodes = List.fold_left label_nodes stmt_nodes stmt.labels in let ctrl_node_set = add_indirect ctrl_node_set in let add_node_ctrl_dpds stmt_node = BoolNodeSet.iter (fun (_, n) -> add_ctrl_dpd pdg stmt_node n) ctrl_node_set in List.iter add_node_ctrl_dpds stmt_nodes in Stmt.Hashtbl.iter add_stmt_ctrl_dpd pdg.ctrl_dpds; Stmt.Hashtbl.clear pdg.ctrl_dpds let process_declarations pdg ~formals ~locals = (* 2 new nodes for each formal parameters : one for its declaration, and one for its values. This is because it might be the case that we only need the declaration whatever the value is. Might allow us to do a better slicing of the callers. TODO: normally, the value should depend on the the declaration, but because we don't know how to select a declaration without selecting the value at the moment, we do the dependence the other way round. *) let do_param (n, state) v = let decl_node = decl_var pdg v in let new_node = add_elem pdg (Key.param_key n) in add_decl_dpd pdg new_node Dpd.Addr decl_node ; add_decl_dpd pdg decl_node Dpd.Addr new_node ; let z = Locations.zone_of_varinfo v in let new_state = Pdg_state.add_loc_node state ~exact:true z new_node in (n+1, new_state) in let _next_in_num, new_state = List.fold_left do_param (1, Pdg_state.empty) formals in List.iter (fun v -> ignore (decl_var pdg v)) locals; new_state let ctrl_call_node pdg call_stmt = try FctIndex.find_info pdg.index (Key.call_ctrl_key call_stmt) with Not_found -> assert false let process_call_args pdg d_state stmt args_dpds : arg_nodes = let num = ref 1 in let process_arg (dpds, decl_dpds) = let new_node = add_elem pdg (Key.call_input_key stmt !num) in add_dpds pdg new_node Dpd.Data d_state dpds; add_decl_dpds pdg new_node Dpd.Data decl_dpds; incr num; new_node in List.map process_arg args_dpds (** Add a PDG node for each formal argument, * and add its dependencies to the corresponding argument node. *) let process_call_params pdg d_state stmt called_kf (arg_nodes:arg_nodes) = let ctrl_node = ctrl_call_node pdg stmt in let param_list = Kernel_function.get_formals called_kf in let process_param state param arg = let new_node = arg in add_ctrl_dpd pdg new_node ctrl_node; let z = Locations.zone_of_varinfo param in Pdg_state.add_loc_node state z new_node ~exact:true in let rec do_param_arg state param_list (arg_nodes: arg_nodes) = match param_list, arg_nodes with | [], [] -> state | p :: param_list, a :: arg_nodes -> let state = process_param state p a in do_param_arg state param_list arg_nodes | [], _ -> (* call to a variadic function *) (* warning already sent during 'from' computation. *) state | _, [] -> Pdg_parameters.fatal "call to a function with to few arguments" in do_param_arg d_state param_list arg_nodes let create_call_output_node pdg state stmt out_key out_from fct_dpds = let new_node = add_elem pdg out_key in add_dpds pdg new_node Dpd.Data state out_from; add_dpds pdg new_node Dpd.Ctrl state fct_dpds; let ctrl_node = ctrl_call_node pdg stmt in add_ctrl_dpd pdg new_node ctrl_node; new_node (** creates a node for lval : caller has to add dpds about the right part *) let create_lval_node pdg state key ~l_loc ~exact ~l_dpds ~l_decl = let new_node = add_elem pdg key in add_dpds pdg new_node Dpd.Addr state l_dpds; add_decl_dpds pdg new_node Dpd.Addr l_decl; let new_state = Pdg_state.add_loc_node state ~exact l_loc new_node in (new_node, new_state) let add_from pdg state_before state lval (default, deps) = let new_node = add_elem pdg (Key.out_from_key lval) in let exact = (not default) in let state = Pdg_state.add_loc_node state ~exact lval new_node in add_dpds pdg new_node Dpd.Data state_before deps; state let process_call_output pdg state_before_call state stmt out default from_out fct_dpds = let exact = (not default) in debug "call-%d Out : %a From %a (%sexact)@." stmt.sid Locations.Zone.pretty out Locations.Zone.pretty from_out (if exact then "" else "not "); let key = Key.call_output_key stmt out in let new_node = create_call_output_node pdg state_before_call stmt key from_out fct_dpds in let state = Pdg_state.add_loc_node state ~exact out new_node in state (** mix between process_call_output and process_asgn *) let process_call_return pdg state_before_call state_with_inputs stmt ~l_loc ~exact ~l_dpds ~l_decl ~r_dpds fct_dpds = let out_key = Key.call_outret_key stmt in let new_node = create_call_output_node pdg state_with_inputs stmt out_key r_dpds fct_dpds in add_dpds pdg new_node Dpd.Addr state_before_call l_dpds; add_decl_dpds pdg new_node Dpd.Addr l_decl; let new_state = Pdg_state.add_loc_node state_before_call ~exact l_loc new_node in new_state (** for skip statement : we want to add a node in the PDG in order to be able * to store information (like marks) about this statement later on *) let process_skip pdg state stmt = ignore (add_elem pdg (Key.stmt_key stmt)); state (** for asm: similar to [process_skip], except that we emit a warning *) let process_asm pdg state stmt = Pdg_parameters.warning ~once:true ~current:true "Ignoring inline assembly code"; ignore (add_elem pdg (Key.stmt_key stmt)); state let add_label pdg label label_stmt = let key = Key.label_key label_stmt label in try FctIndex.find_info pdg.index key with Not_found -> add_elem pdg key let process_stmt_labels pdg stmt = let add label = match label with | Label _ -> ignore (add_label pdg label stmt) | _ -> (* see [add_dpd_switch_cases] *) () in List.iter add stmt.labels let add_label_and_dpd pdg label label_stmt jump_node = let label_node = add_label pdg label label_stmt in add_ctrl_dpd pdg jump_node label_node let add_dpd_goto_label pdg goto_node dest_goto = let rec pickLabel = function | [] -> None | Label _ as lab :: _ -> Some lab | _ :: rest -> pickLabel rest in let label = match pickLabel dest_goto.labels with | Some label -> label | None -> (* break and continue might not jump to a stmt with label : create one*) let lname = Printf.sprintf "fc_stmt_%d" dest_goto.sid in let label = Label (lname, Cil_datatype.Stmt.loc dest_goto, false) in dest_goto.labels <- label::dest_goto.labels; label in add_label_and_dpd pdg label dest_goto goto_node let add_dpd_switch_cases pdg switch_node case_stmts = let add_case stmt = let rec pickLabel = function | [] -> None | Case _ as lab :: _ -> Some lab | Default _ as lab :: _ -> Some lab | _ :: rest -> pickLabel rest in match pickLabel stmt.labels with | Some label -> add_label_and_dpd pdg label stmt switch_node | None -> assert false (* switch sans case ou default ??? *) in List.iter add_case case_stmts (** The control dependencies are stored : they will be added at the end by [finalize_pdg] *) let store_ctrl_dpds pdg node iterator (real_dpd, controlled_stmt) = debug2 "store_ctrl_dpds on %a (real = %b)@." (pretty_node ~key:true) node real_dpd ; let add_ctrl_dpd stmt = let new_dpds = try let old_dpds = Stmt.Hashtbl.find pdg.ctrl_dpds stmt in BoolNodeSet.add (real_dpd, node) old_dpds with Not_found -> BoolNodeSet.singleton (real_dpd, node) in Stmt.Hashtbl.replace pdg.ctrl_dpds stmt new_dpds in iterator add_ctrl_dpd controlled_stmt let mk_jump_node pdg stmt controlled_stmts = let new_node = add_elem pdg (Key.stmt_key stmt) in begin match stmt.skind with | If _ | Loop _ | Return _ -> () | Break _ | Continue _ -> (* can use : add_dpd_goto_label pdg new_node s * if we want later to change break and continue to goto... *) () | Goto (sref,_) -> add_dpd_goto_label pdg new_node !sref | Switch (_,_,stmts,_) -> add_dpd_switch_cases pdg new_node stmts | _ -> assert false end; store_ctrl_dpds pdg new_node Stmt.Hptset.iter controlled_stmts; new_node (** Add a node for a stmt that is a jump. Add control dependencies from this node to the nodes which correspond to the stmt list. Also add dependencies for the jump to the label. Don't use for jumps with data dependencies : use [process_jump_with_exp] instead ! *) let process_jump pdg stmt controlled_stmts = ignore (mk_jump_node pdg stmt controlled_stmts) (** like [process_jump] but also add data dependencies on the data and their declarations. Use for conditional jumps and returns. *) let process_jump_with_exp pdg stmt controlled_stmts state loc_cond decls_cond = let jump_node = mk_jump_node pdg stmt controlled_stmts in add_dpds pdg jump_node Dpd.Data state loc_cond; add_decl_dpds pdg jump_node Dpd.Data decls_cond let add_blk_ctrl_dpds pdg key bstmts = let new_node = add_elem pdg key in store_ctrl_dpds pdg new_node List.iter (true, bstmts) let process_block pdg stmt blk = add_blk_ctrl_dpds pdg (Key.stmt_key stmt) blk.bstmts let process_entry_point pdg bstmts = add_blk_ctrl_dpds pdg Key.entry_point bstmts let create_fun_output_node pdg state dpds = let new_node = add_elem pdg Key.output_key in match state with | Some state -> add_dpds pdg new_node Dpd.Data state dpds | None -> (* return is unreachable *) () let find_return_lval kf = let stmt = Kernel_function.find_return kf in match stmt with | { skind = Return (Some {enode = Lval lval}, _) } -> stmt, lval | _ -> assert false (** add a node corresponding to the returned value. *) let add_retres pdg state ret_stmt retres_loc_dpds retres_decls = let key_return = Key.stmt_key ret_stmt in let return_node = add_elem pdg key_return in let retres = let stmt, lval = find_return_lval pdg.fct in Eva.Results.(before stmt |> eval_address ~for_writing:false lval |> as_zone) in add_dpds pdg return_node Dpd.Data state retres_loc_dpds; add_decl_dpds pdg return_node Dpd.Data retres_decls; let new_state = Pdg_state.add_loc_node state ~exact:true retres return_node in create_fun_output_node pdg (Some new_state) retres; new_state (** part of [finalize_pdg] : add missing inputs * and build a state with the new nodes to find them back when searching for * undefined zones. * (notice that now, they can overlap, for example we can have G and G.a) * And also deals with warning for uninitialized local variables. *) let process_other_inputs pdg = debug2 "process_other_inputs@."; let rec add n dpd_kind (state, zones) z_or_top = (* be careful because [z] can intersect several elements in [zones] *) match zones with | [] -> let key = Key.implicit_in_key z_or_top in let nz = add_elem pdg key in debug "add_implicit_input : %a@." Locations.Zone.pretty z_or_top ; let state = Pdg_state.add_init_state_input state z_or_top nz in add_z_dpd pdg n dpd_kind None nz; state, [(z_or_top, nz)] | (zone, nz)::tl_zones -> match z_or_top, zone with | (Locations.Zone.Top (_,_), Locations.Zone.Top (_,_)) -> add_z_dpd pdg n dpd_kind None nz; (state, zones) | (z, _) when (Locations.Zone.equal zone z) -> add_z_dpd pdg n dpd_kind None nz; (* don't add z : already in *) (state, zones) | _ -> (* rec : look for z in tail *) let state, tl_zones = add n dpd_kind (state, tl_zones) z_or_top in state, (zone, nz)::tl_zones in let add_zone acc (n, dpd_kind, z) = let do_add = match get_var_base z with | Some v -> if Kernel_function.is_local v pdg.fct then false else true | None -> true in if do_add then let acc = match z with | Locations.Zone.Top (_,_) -> add n dpd_kind acc z | _ -> let aux b intervs acc = let z = Locations.Zone.inject b intervs in add n dpd_kind acc z in Locations.Zone.fold_i aux z acc in acc else begin debug2 "might use uninitialized : %a" Locations.Zone.pretty z; acc end in let (state, _) = List.fold_left add_zone (Pdg_state.empty, []) pdg.other_inputs in state (** to call then the building process is over : add the control dependencies in the graph. @return the real PDG that will be used later on. @param from_opt for undefined functions (declarations) *) let finalize_pdg pdg from_opt = debug2 "try to finalize_pdg"; let last_state = try Some (Pdg_state.get_last_state pdg.states) with Not_found -> let ret = try Kernel_function.find_return pdg.fct with Kernel_function.No_Statement -> Pdg_parameters.abort "No return in a declaration" in Pdg_parameters.warning ~once:true ~source:(fst (Stmt.loc ret)) "no final state. Probably unreachable..."; None in (match from_opt with | None -> () (* defined function : retres already processed. *) | Some froms -> (* undefined function : add output 0 *) (* TODO : also add the nodes for the other from ! *) let state = match last_state with Some s -> s | None -> assert false in let process_out out deps s = let open Eva.Assigns.DepsOrUnassigned in if (equal Unassigned deps) then s else let from_out = to_zone deps in let default = may_be_unassigned deps in add_from pdg state s out (default, from_out) in let from_table = froms.Eva.Assigns.memory in let new_state = if Eva.Assigns.Memory.is_bottom from_table then Pdg_state.bottom else let new_state = match from_table with | Top -> process_out Locations.Zone.top Eva.Assigns.DepsOrUnassigned.top state | Map m -> Eva.Assigns.Memory.fold_fuse_same process_out m state | Bottom -> assert false (* checked above *) in if not (Kernel_function.returns_void pdg.fct) then begin let deps_ret = froms.Eva.Assigns.return in let deps_ret = Eva.Deps.to_zone deps_ret in ignore (create_fun_output_node pdg (Some new_state) deps_ret) end; new_state in Pdg_state.store_last_state pdg.states new_state); let init_state = process_other_inputs pdg in Pdg_state.store_init_state pdg.states init_state; add_ctrl_dpds pdg ; debug2 "finalize_pdg ok"; PdgTypes.Pdg.make pdg.fct pdg.graph pdg.states pdg.index (*-----------------------------------------------------------------------*) (** gives needed informations about [lval] : = location + exact + dependencies + declarations *) let get_lval_infos lval stmt = let decl = Cil.extract_varinfos_from_lval lval in let request = Eva.Results.before stmt in let address = Eva.Results.eval_address ~for_writing:true lval request in let z_loc = Eva.Results.as_zone address in let exact = Eva.Results.is_singleton address in let dpds = Eva.Results.address_deps lval request in (z_loc, exact, dpds, decl) (** process assignment {v lval = exp; v} Use the state at ki (before assign) and returns the new state (after assign). *) let process_asgn pdg state stmt lval exp = let r_dpds = Eva.Results.(before stmt |> expr_deps exp) in let r_decl = Cil.extract_varinfos_from_exp exp in let (l_loc, exact, l_dpds, l_decl) = get_lval_infos lval stmt in let key = Key.stmt_key stmt in let new_node, new_state = create_lval_node pdg state key ~l_loc ~exact ~l_dpds ~l_decl in add_dpds pdg new_node Dpd.Data state r_dpds; add_decl_dpds pdg new_node Dpd.Data r_decl; new_state (** Add a PDG node and its dependencies for each explicit call argument. *) let process_args pdg st stmt argl = let process_one_arg arg = let dpds = Eva.Results.(before stmt |> expr_deps arg) in let decl_dpds = Cil.extract_varinfos_from_exp arg in (dpds, decl_dpds) in let arg_dpds = List.map process_one_arg argl in process_call_args pdg st stmt arg_dpds (** Add nodes for the call outputs, and add the dependencies according to from_table. To avoid mixing inputs and outputs, [in_state] is the input state and [new_state] the state to modify. * Process call outputs (including returned value) *) let call_outputs pdg state_before_call state_with_inputs stmt lvaloption assigns fct_dpds = (* obtain inputs from state_with_inputs to avoid mixing in and out *) let Eva.Assigns.{ return = return_deps; memory = memory_deps } = assigns in let print_outputs fmt = Format.fprintf fmt "call outputs : %a" Eva.Assigns.Memory.pretty memory_deps; if not (lvaloption = None) then Format.fprintf fmt "\t and \\result %a@." Eva.Deps.pretty return_deps in debug "%t" print_outputs; let process_out out deps state = if Eva.Assigns.DepsOrUnassigned.(equal Unassigned deps) then state else let from_out = Eva.Assigns.DepsOrUnassigned.to_zone deps in let default = Eva.Assigns.DepsOrUnassigned.may_be_unassigned deps in process_call_output pdg state_with_inputs state stmt out default from_out fct_dpds in if Eva.Assigns.Memory.is_bottom memory_deps then Pdg_state.bottom else let state_with_outputs = match memory_deps with | Top -> process_out Locations.Zone.top Eva.Assigns.DepsOrUnassigned.top state_before_call | Bottom -> assert false (* checked above *) | Map m -> Eva.Assigns.Memory.fold_fuse_same process_out m state_before_call in match lvaloption with | None -> state_with_outputs | Some lval -> let r_dpds = Eva.Deps.to_zone return_deps in let (l_loc, exact, l_dpds, l_decl) = get_lval_infos lval stmt in process_call_return pdg state_with_outputs state_with_inputs stmt ~l_loc ~exact ~l_dpds ~l_decl ~r_dpds fct_dpds (** process call : {v lvaloption = funcexp (argl); v} Use the state at ki (before the call) and returns the new state (after the call). *) let process_call pdg state stmt lvaloption funcexp argl _loc = let state_before_call = state in (* add a simple node for each call in order to have something in the PDG for this statement even if there are no input/output *) ignore (add_elem pdg (Key.call_ctrl_key stmt)); let arg_nodes = process_args pdg state_before_call stmt argl in let state_with_args = state in let called_functions = Eva.Results.callee stmt in let funcexp_dpds = match funcexp.enode with | Lval lval -> Eva.Results.(before stmt |> address_deps lval) | _ -> assert false in let mixed_froms = try let froms = From.Callwise.find (Kstmt stmt) in Some froms with Not_found -> None (* don't have callwise analysis (-calldeps option) *) in let process_simple_call acc called_kf = let state_with_inputs = process_call_params pdg state_with_args stmt called_kf arg_nodes in let r = match mixed_froms with | Some _ -> state_with_inputs (* process outputs later *) | None -> (* don't have callwise analysis (-calldeps option) *) let froms = From.get called_kf in let state_for_this_call = call_outputs pdg state_before_call state_with_inputs stmt lvaloption froms funcexp_dpds in state_for_this_call in r :: acc in let state_for_each_call = List.fold_left process_simple_call [] called_functions in let new_state = match state_for_each_call with | [] -> let stmt_str = Format.asprintf "%a" Printer.pp_stmt stmt in Pdg_parameters.not_yet_implemented ~source:(fst (Cil_datatype.Stmt.loc stmt)) "pdg with an unknown function call: %s" stmt_str | st :: [] -> st | st :: other_states -> let merge s1 s2 = let _,s = Pdg_state.test_and_merge ~old:s1 s2 in s in List.fold_left merge st other_states in let new_state = match mixed_froms with | None -> new_state | Some froms -> call_outputs pdg state_before_call new_state stmt lvaloption froms funcexp_dpds in new_state (** Add a node in the PDG for the conditional statement, * and register the statements that are control-dependent on it. *) let process_condition ctrl_dpds_infos pdg state stmt condition = let loc_cond = Eva.Results.(before stmt |> expr_deps condition) in let decls_cond = Cil.extract_varinfos_from_exp condition in let controlled_stmts = CtrlDpds.get_if_controlled_stmts ctrl_dpds_infos stmt in let go_then, go_else = Eva.Results.condition_truth_value stmt in let real = go_then && go_else (* real dpd if we can go in both branches *) in if not real then debug "[process_condition] stmt %d is not a real cond (never goes in '%s')@." stmt.sid (if go_then then "else" else "then"); (* build a node for the condition and store de control dependencies *) process_jump_with_exp pdg stmt (real, controlled_stmts) state loc_cond decls_cond (** let's add a node for e jump statement (goto, break, continue) and find the statements which are depending on it. Returns are not handled here, but in {!Build.process_return}. *) let process_jump_stmt pdg ctrl_dpds_infos jump = let controlled_stmts = CtrlDpds.get_jump_controlled_stmts ctrl_dpds_infos jump in let real = Eva.Results.is_reachable jump in if not real then debug "[process_jump_stmt] stmt %d is not a real jump@." jump.sid; process_jump pdg jump (real, controlled_stmts) (** Loop are processed like gotos because CIL transforms them into * {v while(true) body; v} which is equivalent to {v L : body ; goto L; v} * There is a small difference because we have to detect the case where * the [goto L;] would be unreachable (no real loop). * This is important because it might lead to infinite loop (see bst#787) *) let process_loop_stmt pdg ctrl_dpds_infos loop = let _entry, back_edges = Stmts_graph.loop_preds loop in debug2 "[process_loop_stmt] for loop %d : back edges = {%a}@." loop.sid (Pretty_utils.pp_list Stmt.pretty_sid) back_edges; let controlled_stmts = CtrlDpds.get_loop_controlled_stmts ctrl_dpds_infos loop in let real_loop = List.exists Eva.Results.is_reachable back_edges in if not real_loop then debug "[process_loop_stmt] stmt %d is not a real loop@." loop.sid; process_jump pdg loop (real_loop, controlled_stmts) (** [return ret_exp;] is equivalent to [out0 = ret_exp; goto END;] * while a simple [return;] is only a [goto END;]. * Here, we assume that the {{:../html/Oneret.html}Oneret} analysis * was used, ie. that it is the only return of the function * and that it is the last statement. So, the [goto] is not useful, * and the final state is stored to be used later on to compute the outputs. *) let process_return _current_function pdg state stmt ret_exp = let last_state = match ret_exp with | Some exp -> let loc_exp = Eva.Results.(before stmt |> expr_deps exp) in let decls_exp = Cil.extract_varinfos_from_exp exp in add_retres pdg state stmt loc_exp decls_exp | None -> let controlled_stmt = Cil_datatype.Stmt.Hptset.empty in let real = Eva.Results.is_reachable stmt in process_jump pdg stmt (real, controlled_stmt); state in if Eva.Results.is_reachable stmt then Pdg_state.store_last_state pdg.states last_state module Computer (Initial:sig val initial: (stmt * PdgTypes.data_state) list end) (Fenv:Dataflows.FUNCTION_ENV) (Param:sig val current_pdg : pdg_build val ctrl_dpds_infos : CtrlDpds.t end) = struct let pdg_debug fmt = debug fmt type t = PdgTypes.data_state let current_pdg = Param.current_pdg let current_function = Fenv.kf;; assert (current_function == current_pdg.fct);; let ctrl_dpds_infos = Param.ctrl_dpds_infos let init = Initial.initial;; let bottom = Pdg_state.bottom let pretty fmt (v: t) = Format.fprintf fmt "<STATE>@\n%a@\n<\\STATE>@." Pdg_state.pretty v let join_and_is_included smaller larger = pdg_debug "smaller (new): %a larger (old) %a" pretty smaller pretty larger; let is_new, new_state = Pdg_state.test_and_merge ~old:larger smaller in pdg_debug "new_state: %a is_new: %b" pretty new_state is_new; (new_state, not is_new) ;; let join a b = fst (join_and_is_included a b) let is_included a b = snd (join_and_is_included a b) let rec process_init current_pdg state stmt lv = function | SingleInit e -> process_asgn current_pdg state stmt lv e | CompoundInit (_,l) -> List.fold_left (fun acc (o,i) -> let lv = Cil.addOffsetLval o lv in process_init current_pdg acc stmt lv i) state l (** Compute the new state after 'instr' starting from state before 'state'. *) let doInstr stmt instr state = Async.yield (); pdg_debug "doInstr sid:%d : %a" stmt.sid Printer.pp_instr instr; match instr with | _ when not (Eva.Results.is_reachable stmt) -> pdg_debug "stmt sid:%d is unreachable : skip.@." stmt.sid ; Pdg_state.bottom | Local_init (v, AssignInit i, _) -> process_init current_pdg state stmt (Cil.var v) i | Local_init (v, ConsInit (f, args, kind), loc) -> Async.yield (); Cil.treat_constructor_as_func (process_call current_pdg state stmt) v f args kind loc | Set (lv, exp, _) -> process_asgn current_pdg state stmt lv exp | Call (lvaloption,funcexp,argl,loc) -> Async.yield (); process_call current_pdg state stmt lvaloption funcexp argl loc | Code_annot _ | Skip _ -> process_skip current_pdg state stmt | Asm _ -> process_asm current_pdg state stmt (** Called before processing the successors of the statements. *) let transfer_stmt (stmt: Cil_types.stmt) (state: t) = pdg_debug "doStmt %d @." stmt.sid ; let map_on_all_succs newstate = List.map (fun x -> (x,newstate)) stmt.succs in (* Notice that the stmt labels are processed while processing the jumps. *) process_stmt_labels current_pdg stmt; match stmt.skind with | Instr i -> map_on_all_succs (doInstr stmt i state) | Block blk -> process_block current_pdg stmt blk; map_on_all_succs state | UnspecifiedSequence seq -> process_block current_pdg stmt (Cil.block_from_unspecified_sequence seq); map_on_all_succs state | Switch (exp,_,_,_) | If (exp,_,_,_) -> process_condition ctrl_dpds_infos current_pdg state stmt exp; map_on_all_succs state | Return (exp,_) -> process_return current_function current_pdg state stmt exp; [] | Continue _ | Break _ | Goto _ -> process_jump_stmt current_pdg ctrl_dpds_infos stmt; map_on_all_succs state | Loop _ -> process_loop_stmt current_pdg ctrl_dpds_infos stmt; map_on_all_succs state | Throw _ | TryCatch _ -> Pdg_parameters.fatal "Exception node in the AST" | TryExcept (_, _, _, _) | TryFinally (_, _, _) -> map_on_all_succs state end exception Value_State_Top (** Compute and return the PDG for the given function *) let compute_pdg_for_f kf = let pdg = create_pdg_build kf in let f_locals, f_stmts = if Eva.Analysis.use_spec_instead_of_definition kf then [], [] else if Eva.Analysis.save_results kf then let f = Kernel_function.get_definition kf in f.slocals, f.sbody.bstmts else raise Value_State_Top in let init_state = process_entry_point pdg f_stmts; let formals = Kernel_function.get_formals kf in process_declarations pdg ~formals ~locals:f_locals in let froms = match f_stmts with | [] -> Pdg_state.store_last_state pdg.states init_state; let froms = From.get kf in Some (froms) | start :: _ -> let ctrl_dpds_infos = CtrlDpds.compute kf in (* Put all statements in initial, so that they are processed and are in the worklist (even if they are dead). *) let allstmts = (Kernel_function.get_definition kf).sallstmts in let allstmts_no_start = List.filter (fun s -> s.sid != start.sid) allstmts in let initial_list = List.map (fun s -> (s, Pdg_state.bottom)) allstmts_no_start in let module Initial = struct let initial = (start, init_state)::initial_list end in let module Fenv = (val Dataflows.function_env kf: Dataflows.FUNCTION_ENV) in let module Computer = Computer(Initial)(Fenv)(struct let current_pdg = pdg let ctrl_dpds_infos = ctrl_dpds_infos end) in if Eva.Results.is_reachable start then begin let module Compute = Dataflows.Simple_forward(Fenv)(Computer) in Array.iteri (fun ord value -> let stmt = Fenv.to_stmt ord in Stmt.Hashtbl.replace pdg.states stmt value) Compute.before; None end else raise (Err_Bot (Printf.sprintf "unreachable entry point (sid:%d, function %s)" start.sid (Kernel_function.get_name kf))) in let pdg = finalize_pdg pdg froms in pdg let degenerated top kf = Pdg_parameters.feedback "%s for function %a" (if top then "Top" else "Bottom") Kernel_function.pretty kf; if top then PdgTypes.Pdg.top kf else PdgTypes.Pdg.bottom kf let compute_pdg kf = if not (Eva.Analysis.is_computed ()) then Eva.Analysis.compute (); Pdg_parameters.feedback "computing for function %a" Kernel_function.pretty kf; try if is_variadic kf then Pdg_parameters.not_yet_implemented "variadic function"; let pdg = compute_pdg_for_f kf in Pdg_parameters.feedback "done for function %a" Kernel_function.pretty kf; pdg with | Err_Bot what -> Pdg_parameters.warning "%s" what ; degenerated false kf | Value_State_Top -> degenerated true kf | Log.AbortFatal what -> (* [JS 2012/08/24] nobody should catch this exception *) Pdg_parameters.warning "internal error: %s" what ; degenerated true kf | Log.AbortError what -> (* [JS 2012/08/24] nobody should catch this exception *) Pdg_parameters.warning "user error: %s" what ; degenerated true kf | Pdg_state.Cannot_fold -> Pdg_parameters.warning "too imprecise value analysis : abort" ; degenerated true kf | Log.FeatureRequest (source, who, what) -> (* [JS 2012/08/24] nobody should catch this exception *) Pdg_parameters.warning ?source "not implemented by %s yet: %s" who what; degenerated true kf
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>