diff --git a/infer/bin/inferlib.py b/infer/bin/inferlib.py index 2139f0d82..88eadc49d 100644 --- a/infer/bin/inferlib.py +++ b/infer/bin/inferlib.py @@ -281,7 +281,8 @@ def should_report(analyzer, row): 'RETAIN_CYCLE', 'ASSERTION_FAILURE', 'ACTIVITY_LEAK', - 'BAD_POINTER_COMPARISON' + 'BAD_POINTER_COMPARISON', + 'CHECKERS_PRINTF_ARGS' ] if analyzer in [ERADICATE, CHECKERS, TRACING]: diff --git a/infer/src/backend/symExec.ml b/infer/src/backend/symExec.ml index fced9f84f..d727c6f0d 100644 --- a/infer/src/backend/symExec.ml +++ b/infer/src/backend/symExec.ml @@ -826,6 +826,9 @@ let normalize_params pdesc prop actual_params = let prop, args = IList.fold_left norm_arg (prop, []) actual_params in (prop, IList.rev args) +let do_error_checks node instr pname pdesc = + if !Config.curr_language = Config.Java then PrintfArgs.check_printf_args_ok node instr pname pdesc + (** Execute [instr] with a symbolic heap [prop].*) let rec sym_exec cfg tenv pdesc _instr (_prop: Prop.normal Prop.t) path : (Prop.normal Prop.t * Paths.Path.t) list = @@ -948,6 +951,7 @@ let rec sym_exec cfg tenv pdesc _instr (_prop: Prop.normal Prop.t) path sym_exe_builtin cfg pdesc instr tenv _prop path ret_ids args callee_pname loc | Sil.Call (ret_ids, Sil.Const (Sil.Cfun callee_pname), actual_params, loc, call_flags) -> (** Generic fun call with known name *) + do_error_checks (Paths.Path.curr_node path) instr pname pdesc; let (prop_r, _n_actual_params) = normalize_params pname _prop actual_params in let fn, n_actual_params = handle_special_cases_call tenv cfg callee_pname _n_actual_params in let resolved_pname = diff --git a/infer/src/checkers/printfArgs.ml b/infer/src/checkers/printfArgs.ml index 9bff7c28f..128daf566 100644 --- a/infer/src/checkers/printfArgs.ml +++ b/infer/src/checkers/printfArgs.ml @@ -122,13 +122,11 @@ let rec format_string_type_names let printf_args_name = "CHECKERS_PRINTF_ARGS" -let callback_printf_args - (all_procs : Procname.t list) - (get_proc_desc: Procname.t -> Cfg.Procdesc.t option) - (idenv: Idenv.t) - (tenv: Sil.tenv) +let check_printf_args_ok + (node: Cfg.Node.t) + (instr: Sil.instr) (proc_name: Procname.t) - (proc_desc : Cfg.Procdesc.t) : unit = + (proc_desc: Cfg.Procdesc.t): unit = (* Check if format string lines up with arguments *) let rec check_type_names instr_loc n_arg instr_proc_name fmt_type_names arg_type_names = @@ -184,44 +182,49 @@ let callback_printf_args | Sil.Const c -> PatternMatch.java_get_const_type_name c | _ -> raise (Failure "Could not resolve fixed type name") in - let do_instr - (node: Cfg.Node.t) - (instr: Sil.instr): unit = - match instr with - | Sil.Call (_, Sil.Const (Sil.Cfun pn), args, cl, _) -> ( - match printf_like_function pn with - | Some printf -> ( - try - let fmt, fixed_nvars, array_nvar = format_arguments printf args in - let instrs = Cfg.Node.get_instrs node in - let fixed_nvar_type_names = IList.map (fixed_nvar_type_name instrs) fixed_nvars in - let vararg_ivar_type_names = match array_nvar with - | Some nvar -> ( - let ivar = array_ivar instrs nvar in - PatternMatch.get_vararg_type_names node ivar) - | None -> [] in - match fmt with - | Some fmt -> - check_type_names - cl - (printf.format_pos + 1) - pn - (format_string_type_names fmt 0) - (fixed_nvar_type_names@ vararg_ivar_type_names) - | None -> - Checkers.ST.report_error - proc_name - proc_desc - printf_args_name - cl - "Format string must be string literal" - with e -> - L.stderr - "%s Exception when analyzing %s: %s@." - printf_args_name - (Procname.to_string proc_name) - (Printexc.to_string e)) - | None -> ()) - | _ -> () in - - Cfg.Procdesc.iter_instrs do_instr proc_desc + match instr with + | Sil.Call (_, Sil.Const (Sil.Cfun pn), args, cl, _) -> ( + match printf_like_function pn with + | Some printf -> ( + try + let fmt, fixed_nvars, array_nvar = format_arguments printf args in + let instrs = Cfg.Node.get_instrs node in + let fixed_nvar_type_names = IList.map (fixed_nvar_type_name instrs) fixed_nvars in + let vararg_ivar_type_names = match array_nvar with + | Some nvar -> ( + let ivar = array_ivar instrs nvar in + PatternMatch.get_vararg_type_names node ivar) + | None -> [] in + match fmt with + | Some fmt -> + check_type_names + cl + (printf.format_pos + 1) + pn + (format_string_type_names fmt 0) + (fixed_nvar_type_names@ vararg_ivar_type_names) + | None -> + Checkers.ST.report_error + proc_name + proc_desc + printf_args_name + cl + "Format string must be string literal" + with e -> + L.stderr + "%s Exception when analyzing %s: %s@." + printf_args_name + (Procname.to_string proc_name) + (Printexc.to_string e)) + | None -> ()) + | _ -> () + +let callback_printf_args + (all_procs : Procname.t list) + (get_proc_desc: Procname.t -> Cfg.Procdesc.t option) + (idenv: Idenv.t) + (tenv: Sil.tenv) + (proc_name: Procname.t) + (proc_desc : Cfg.Procdesc.t) : unit = + Cfg.Procdesc.iter_instrs (fun n i -> check_printf_args_ok n i proc_name proc_desc) proc_desc + diff --git a/infer/src/checkers/printfArgs.mli b/infer/src/checkers/printfArgs.mli index 71c5b28fa..ae2865981 100644 --- a/infer/src/checkers/printfArgs.mli +++ b/infer/src/checkers/printfArgs.mli @@ -17,5 +17,6 @@ type printf_signature = { val add_printf_like_function : printf_signature -> unit +val check_printf_args_ok : Cfg.Node.t -> Sil.instr -> Procname.t -> Cfg.Procdesc.t -> unit val callback_printf_args: Callbacks.proc_callback_t