Reviewed By: jeremydubreil Differential Revision: D6321699 fbshipit-source-id: 2bcfb71master
							parent
							
								
									a1010fb9fc
								
							
						
					
					
						commit
						e2f38423e5
					
				| @ -1,172 +0,0 @@ | ||||
| (* | ||||
|  * Copyright (c) 2014 - present Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  *) | ||||
| 
 | ||||
| open! IStd | ||||
| module L = Logging | ||||
| module F = Format | ||||
| 
 | ||||
| (** Extension for the repeated calls check. *) | ||||
| module RepeatedCallsExtension : Eradicate.ExtensionT = struct | ||||
|   module InstrSet = Caml.Set.Make (struct | ||||
|     type t = Sil.instr | ||||
| 
 | ||||
|     let compare i1 i2 = | ||||
|       match (i1, i2) with | ||||
|       | Sil.Call (_, e1, etl1, _, cf1), Sil.Call (_, e2, etl2, _, cf2) -> | ||||
|           (* ignore return ids and call flags *) | ||||
|           [%compare : Exp.t * (Exp.t * Typ.t) list * CallFlags.t] (e1, etl1, cf1) (e2, etl2, cf2) | ||||
|       | _ -> | ||||
|           Sil.compare_instr i1 i2 | ||||
| 
 | ||||
|   end) | ||||
| 
 | ||||
|   type extension = InstrSet.t | ||||
| 
 | ||||
|   let empty = InstrSet.empty | ||||
| 
 | ||||
|   let join calls1 calls2 = InstrSet.inter calls1 calls2 | ||||
| 
 | ||||
|   let pp fmt calls = | ||||
|     let pp_call instr = F.fprintf fmt "  %a@\n" (Sil.pp_instr Pp.text) instr in | ||||
|     if not (InstrSet.is_empty calls) then ( F.fprintf fmt "Calls:@\n" ; InstrSet.iter pp_call calls ) | ||||
| 
 | ||||
| 
 | ||||
|   let get_old_call instr calls = try Some (InstrSet.find instr calls) with Not_found -> None | ||||
| 
 | ||||
|   let add_call instr calls = if InstrSet.mem instr calls then calls else InstrSet.add instr calls | ||||
| 
 | ||||
|   type paths = | ||||
|     | AllPaths  (** Check on all paths *) | ||||
|     | SomePath  (** Check if some path exists *) | ||||
|     [@@deriving compare] | ||||
| 
 | ||||
|   let equal_paths = [%compare.equal : paths] | ||||
| 
 | ||||
|   (** Check if the procedure performs an allocation operation. | ||||
|       If [paths] is AllPaths, check if an allocation happens on all paths. | ||||
|       If [paths] is SomePath, check if a path with an allocation exists. *) | ||||
|   let proc_performs_allocation tenv pdesc paths : Location.t option = | ||||
|     let node_allocates node : Location.t option = | ||||
|       let found = ref None in | ||||
|       let proc_is_new pn = | ||||
|         Typ.Procname.equal pn BuiltinDecl.__new || Typ.Procname.equal pn BuiltinDecl.__new_array | ||||
|       in | ||||
|       let do_instr instr = | ||||
|         match instr with | ||||
|         | Sil.Call (_, Exp.Const Const.Cfun pn, _, loc, _) when proc_is_new pn -> | ||||
|             found := Some loc | ||||
|         | _ -> | ||||
|             () | ||||
|       in | ||||
|       List.iter ~f:do_instr (Procdesc.Node.get_instrs node) ; | ||||
|       !found | ||||
|     in | ||||
|     let module DFAllocCheck = Dataflow.MakeDF (struct | ||||
|       type t = Location.t option [@@deriving compare] | ||||
| 
 | ||||
|       let equal = [%compare.equal : t] | ||||
| 
 | ||||
|       let join_ paths_ l1o l2o = | ||||
|         (* join with left priority *) | ||||
|         match (l1o, l2o) with | ||||
|         | None, None -> | ||||
|             None | ||||
|         | Some loc, None | None, Some loc -> | ||||
|             if equal_paths paths_ AllPaths then None else Some loc | ||||
|         | Some loc1, Some _ -> | ||||
|             Some loc1 | ||||
| 
 | ||||
| 
 | ||||
|       (* left priority *) | ||||
|       let join = join_ paths | ||||
| 
 | ||||
|       let do_node _ node lo1 = | ||||
|         let lo2 = node_allocates node in | ||||
|         let lo' = | ||||
|           (* use left priority join to implement transfer function *) | ||||
|           join_ SomePath lo1 lo2 | ||||
|         in | ||||
|         ([lo'], [lo']) | ||||
| 
 | ||||
| 
 | ||||
|       let proc_throws _ = Dataflow.DontKnow | ||||
|     end) in | ||||
|     let transitions = DFAllocCheck.run tenv pdesc None in | ||||
|     match transitions (Procdesc.get_exit_node pdesc) with | ||||
|     | DFAllocCheck.Transition (loc, _, _) -> | ||||
|         loc | ||||
|     | DFAllocCheck.Dead_state -> | ||||
|         None | ||||
| 
 | ||||
| 
 | ||||
|   (** Check repeated calls to the same procedure. *) | ||||
|   let check_instr tenv get_proc_desc curr_pname curr_pdesc extension instr normalized_etl = | ||||
|     (* Arguments are not temporary variables. *) | ||||
|     let arguments_not_temp args = | ||||
|       let filter_arg (e, _) = | ||||
|         match e with | ||||
|         | Exp.Lvar pvar -> | ||||
|             (* same temporary variable does not imply same value *) | ||||
|             not (Pvar.is_frontend_tmp pvar) | ||||
|         | _ -> | ||||
|             true | ||||
|       in | ||||
|       List.for_all ~f:filter_arg args | ||||
|     in | ||||
|     match instr with | ||||
|     | Sil.Call ((Some _ as ret_id), Exp.Const Const.Cfun callee_pname, _, loc, call_flags) | ||||
|       when arguments_not_temp normalized_etl -> | ||||
|         let instr_normalized_args = | ||||
|           Sil.Call (ret_id, Exp.Const (Const.Cfun callee_pname), normalized_etl, loc, call_flags) | ||||
|         in | ||||
|         let report proc_desc = | ||||
|           match get_old_call instr_normalized_args extension with | ||||
|           | Some Sil.Call (_, _, _, loc_old, _) -> ( | ||||
|             match proc_performs_allocation tenv proc_desc AllPaths with | ||||
|             | Some alloc_loc -> | ||||
|                 let description = | ||||
|                   Format.asprintf "call to %s seen before on line %d (may allocate at %a:%d)" | ||||
|                     (Typ.Procname.to_simplified_string callee_pname) | ||||
|                     loc_old.Location.line SourceFile.pp alloc_loc.Location.file | ||||
|                     alloc_loc.Location.line | ||||
|                 in | ||||
|                 Checkers.ST.report_error tenv curr_pname curr_pdesc | ||||
|                   IssueType.checkers_repeated_calls loc description | ||||
|             | None -> | ||||
|                 () ) | ||||
|           | _ -> | ||||
|               () | ||||
|         in | ||||
|         let () = | ||||
|           match get_proc_desc callee_pname with | ||||
|           | None -> | ||||
|               () | ||||
|           | Some proc_desc -> | ||||
|               if Procdesc.is_defined proc_desc then report proc_desc | ||||
|         in | ||||
|         add_call instr_normalized_args extension | ||||
|     | _ -> | ||||
|         extension | ||||
| 
 | ||||
| 
 | ||||
|   let ext = {TypeState.empty; check_instr; join; pp} | ||||
| 
 | ||||
|   let update_payload _ payload = payload | ||||
| end | ||||
| 
 | ||||
| (* CheckRepeatedCalls *) | ||||
| 
 | ||||
| module MainRepeatedCalls = Eradicate.Build (RepeatedCallsExtension) | ||||
| 
 | ||||
| let callback_check_repeated_calls callback_args = | ||||
|   let checks = | ||||
|     {TypeCheck.eradicate= false; check_extension= Config.repeated_calls; check_ret_type= []} | ||||
|   in | ||||
|   MainRepeatedCalls.callback checks callback_args | ||||
| 
 | ||||
| @ -1,12 +0,0 @@ | ||||
| (* | ||||
|  * Copyright (c) 2014 - present Facebook, Inc. | ||||
|  * All rights reserved. | ||||
|  * | ||||
|  * This source code is licensed under the BSD style license found in the | ||||
|  * LICENSE file in the root directory of this source tree. An additional grant | ||||
|  * of patent rights can be found in the PATENTS file in the same directory. | ||||
|  *) | ||||
| 
 | ||||
| open! IStd | ||||
| 
 | ||||
| val callback_check_repeated_calls : Callbacks.proc_callback_t | ||||
					Loading…
					
					
				
		Reference in new issue