Uninitialized value/variable checker

Reviewed By: sblackshear

Differential Revision: D5804192

fbshipit-source-id: 85126d3
master
Dino Distefano 7 years ago committed by Facebook Github Bot
parent 169df0fe80
commit f87447ba52

@ -47,7 +47,7 @@ BUILD_SYSTEMS_TESTS += \
DIRECT_TESTS += \
c_biabduction c_bufferoverrun c_errors c_frontend \
cpp_bufferoverrun cpp_errors cpp_frontend cpp_liveness cpp_quandary cpp_siof cpp_threadsafety cpp_nullable \
cpp_bufferoverrun cpp_errors cpp_frontend cpp_liveness cpp_quandary cpp_siof cpp_threadsafety cpp_uninit cpp_nullable \
ifneq ($(BUCK),no)
BUILD_SYSTEMS_TESTS += buck-clang-db buck_flavors buck_flavors_run buck_flavors_deterministic

@ -337,7 +337,8 @@ type payload =
; resources: ResourceLeakDomain.summary option
; siof: SiofDomain.astate option
; threadsafety: ThreadSafetyDomain.summary option
; buffer_overrun: BufferOverrunDomain.Summary.t option }
; buffer_overrun: BufferOverrunDomain.Summary.t option
; uninit: UninitDomain.summary option }
type summary =
{ nodes: Procdesc.Node.id list (** ids of cfg nodes of the procedure *)
@ -466,14 +467,15 @@ let pp_payload pe fmt
; siof
; threadsafety
; buffer_overrun
; annot_map } =
; annot_map
; uninit } =
let pp_opt prefix pp fmt = function
| Some x
-> F.fprintf fmt "%s: %a@\n" prefix pp x
| None
-> ()
in
F.fprintf fmt "%a%a%a%a%a%a%a%a@\n"
F.fprintf fmt "%a%a%a%a%a%a%a%a%a@\n"
(pp_opt "PrePosts" (pp_specs pe))
(Option.map ~f:NormSpec.tospecs preposts)
(pp_opt "TypeState" (TypeState.pp TypeState.unit_ext))
@ -482,6 +484,7 @@ let pp_payload pe fmt
(pp_opt "ThreadSafety" ThreadSafetyDomain.pp_summary) threadsafety
(pp_opt "BufferOverrun" BufferOverrunDomain.Summary.pp) buffer_overrun
(pp_opt "AnnotationReachability" AnnotReachabilityDomain.pp) annot_map
(pp_opt "Uninitialised" UninitDomain.pp_summary) uninit
let pp_summary_text fmt summary =
let err_log = summary.attributes.ProcAttributes.err_log in
@ -693,7 +696,8 @@ let empty_payload =
; resources= None
; siof= None
; threadsafety= None
; buffer_overrun= None }
; buffer_overrun= None
; uninit= None }
(** [init_summary (depend_list, nodes,
proc_flags, calls, in_out_calls_opt, proc_attributes)]

@ -137,7 +137,8 @@ type payload =
; resources: ResourceLeakDomain.summary option
; siof: SiofDomain.astate option
; threadsafety: ThreadSafetyDomain.summary option
; buffer_overrun: BufferOverrunDomain.Summary.t option }
; buffer_overrun: BufferOverrunDomain.Summary.t option
; uninit: UninitDomain.summary option }
(** Procedure summary *)
type summary =

@ -660,7 +660,8 @@ and ( annotation_reachability
, resource_leak
, siof
, threadsafety
, suggest_nullable ) =
, suggest_nullable
, uninit ) =
let all_checkers = ref [] in
let default_checkers = ref [] in
let mk_checker ?(default= false) ~long doc =
@ -705,7 +706,7 @@ and ( annotation_reachability
and suggest_nullable =
mk_checker ~long:"suggest-nullable" ~default:false
"Nullable annotation sugesstions analysis (experimental)"
in
and uninit = mk_checker ~long:"uninit" ~default:true "checker for use of uninitialized values" in
let mk_only (var, long) =
let _ : bool ref =
CLOpt.mk_bool_group ~long:(long ^ "-only")
@ -745,7 +746,8 @@ and ( annotation_reachability
, resource_leak
, siof
, threadsafety
, suggest_nullable )
, suggest_nullable
, uninit )
and annotation_reachability_custom_pairs =
CLOpt.mk_json ~long:"annotation-reachability-custom-pairs"
@ -2338,6 +2340,8 @@ and show_progress_bar = !progress_bar
and siof = !siof
and uninit = !uninit
and siof_safe_methods = !siof_safe_methods
and skip_analysis_in_path = !skip_analysis_in_path

@ -625,6 +625,8 @@ val show_progress_bar : bool
val siof : bool
val uninit : bool
val siof_safe_methods : string list
val skip_analysis_in_path : string list

@ -58,6 +58,7 @@ let checkers =
, [(Procedure RepeatedCallsChecker.callback_check_repeated_calls, Config.Java)] )
; ("resource leak", Config.resource_leak, [(Procedure ResourceLeaks.checker, Config.Java)])
; ("SIOF", Config.siof, [(Procedure Siof.checker, Config.Clang)])
; ("uninitialized variables", Config.uninit, [(Procedure Uninit.checker, Config.Clang)])
; ( "thread safety"
, Config.threadsafety
, [ (Procedure ThreadSafety.analyze_procedure, Config.Clang)

@ -0,0 +1,259 @@
(*
* Copyright (c) 2017 - 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 F = Format
module L = Logging
(** Forward analysis to compute uninitialized variables at each program point *)
module D =
UninitDomain.Domain
module UninitVars = AbstractDomain.FiniteSet (Var)
module AliasedVars = AbstractDomain.FiniteSet (UninitDomain.VarPair)
module PrePost = AbstractDomain.Pair (D) (D)
module RecordDomain = UninitDomain.Record (UninitVars) (AliasedVars) (D)
module Summary = Summary.Make (struct
type payload = UninitDomain.summary
let update_payload sum (summary: Specs.summary) =
{summary with payload= {summary.payload with uninit= Some sum}}
let read_payload (summary: Specs.summary) = summary.payload.uninit
end)
let intraprocedural_only = true
(* Check if an expression is in set of variables *)
let exp_in_set exp vset =
let _, pvars = Exp.get_vars exp in
List.exists ~f:(fun pv -> D.mem (Var.of_pvar pv) vset) pvars
let zip_actual_formal_params callee_pname actual_params =
match Ondemand.get_proc_desc callee_pname with
| Some pdesc
-> let formals, _ = List.unzip (Procdesc.get_formals pdesc) in
let actual, _ = List.unzip actual_params in
(List.zip actual formals, actual)
| _
-> (None, [])
let deref_actual_params callee_pname actual_params deref_formal_params =
match zip_actual_formal_params callee_pname actual_params with
| None, _
-> []
| Some assoc_actual_formal, _
-> List.fold
~f:(fun acc (a, f) ->
let fe = Exp.Lvar (Pvar.mk f callee_pname) in
if exp_in_set fe deref_formal_params then a :: acc else acc)
~init:[] assoc_actual_formal
module TransferFunctions (CFG : ProcCfg.S) = struct
module CFG = CFG
(* a state is a pair: ({set of uninitialized locals}, {set of written formal params}) *)
(* module Domain = AbstractDomain.Pair (UninitVars) (PrePost)*)
module Domain = RecordDomain
type extras = ProcData.no_extras
let apply_fun_exp_set f exp vset =
let _, pvars = Exp.get_vars exp in
List.fold ~f:(fun acc_set pvar -> f (Var.of_pvar pvar) acc_set) ~init:vset pvars
let exp_add_set exp vset = apply_fun_exp_set D.add exp vset
let exp_remove_set exp vset = apply_fun_exp_set D.remove exp vset
let set_difference set1 set2 =
List.fold ~f:(fun acc_set e -> exp_remove_set e acc_set) ~init:set1 set2
(* check if its a basic type *)
let rec is_basic_typ t =
match t.Typ.desc with
| Typ.Tint _ | Typ.Tfloat _ | Typ.Tvoid
-> true
| Typ.Tptr (t', _)
-> is_basic_typ t'
| _
-> false
(* returns the formal parameter of a procedure that have a pointer type *)
let ptr_formals pdesc =
let proc_name = Procdesc.get_proc_name pdesc in
let is_ptr (_, t) = match t.Typ.desc with Typ.Tptr _ -> true | _ -> false in
let pfs = List.filter ~f:is_ptr (Procdesc.get_formals pdesc) in
List.map ~f:(fun (name, _) -> Exp.Lvar (Pvar.mk name proc_name)) pfs
(* Update the set of written formals when it finds a sequence
n$0=*&i;
....
*n$0=..;
*)
let update_state_formal node pdesc formal_set =
let ptr_formals = ptr_formals pdesc in
let ids_assigned_to_formals, dereferenced_ids =
List.fold
~f:(fun (acc1, acc2) (i, _) ->
match i with
| Sil.Load (lhs_id, rhs_exp, _, _) when List.mem ptr_formals rhs_exp ~equal:Exp.equal
-> ((lhs_id, rhs_exp) :: acc1, acc2)
| Sil.Store (Exp.Var lhs_id, _, _, _)
-> (acc1, lhs_id :: acc2)
| _
-> (acc1, acc2))
(CFG.instr_ids node) ~init:([], [])
in
List.fold
~f:(fun acc_set (id, formal_param) ->
if List.mem dereferenced_ids id ~equal:Ident.equal then exp_add_set formal_param acc_set
else acc_set)
ids_assigned_to_formals ~init:formal_set
let exec_instr
( {Domain.uninit_vars= uninit; Domain.aliased_vars= _; Domain.prepost= pre, wformals} as
astate ) {ProcData.pdesc} node = function
| Sil.Load (_, rhs_exp, _, _)
-> let post = update_state_formal node pdesc wformals in
let set_ptr_formals = ptr_formals pdesc in
let pre' =
if List.mem set_ptr_formals ~equal:Exp.equal rhs_exp && not (exp_in_set rhs_exp post)
then exp_add_set rhs_exp pre
else pre
in
{astate with prepost= (pre', post)}
| Sil.Store (lhs_exp, _, rhs_exp, _)
-> if exp_in_set rhs_exp uninit then astate
else {astate with Domain.uninit_vars= exp_remove_set lhs_exp uninit}
| Sil.Call (ret_id, Exp.Const Const.Cfun callee_pname, actual_params, _, _)
when not intraprocedural_only
-> (
let astate' =
Option.value_map
~f:(fun (ret_id, _) ->
{astate with Domain.uninit_vars= exp_remove_set (Exp.Var ret_id) uninit})
~default:astate ret_id
in
match
( zip_actual_formal_params callee_pname actual_params
, Summary.read_summary pdesc callee_pname )
with
| (Some assoc_actual_formal, actual), Some {pre= _; post= wformals_callee}
-> let initialized_actual_params =
List.filter
~f:(fun a ->
match List.Assoc.find assoc_actual_formal ~equal:Exp.equal a with
| Some fp
-> D.mem (Var.of_pvar (Pvar.mk fp callee_pname)) wformals_callee
| None
-> false)
actual
in
let uninit' = set_difference uninit initialized_actual_params in
{astate with Domain.uninit_vars= uninit'; Domain.prepost= astate'.prepost}
| _, _
-> L.(debug Linters Medium)
"Warning: unable to deal with callee '%s' summary@\n"
(Typ.Procname.to_string callee_pname) ;
astate' )
| Sil.Call (_, _, actual_params, _, _)
-> (* in case of intraprocedural only analysis we assume that parameters
passed by reference to a function will be initialized inside that function *)
let actual_passed_by_ref =
List.fold
~f:(fun acc (e, t) -> match t.Typ.desc with Typ.Tptr _ -> e :: acc | _ -> acc)
~init:[] actual_params
in
let uninit' = set_difference uninit actual_passed_by_ref in
{astate with Domain.uninit_vars= uninit'}
| Sil.Declare_locals (pvar_list, _)
-> List.fold
~f:(fun astate_acc (pvar, t) ->
if is_basic_typ t then
{ astate_acc with
Domain.uninit_vars= D.add (Var.of_pvar pvar) astate_acc.Domain.uninit_vars }
else astate_acc)
~init:astate pvar_list
| Sil.Remove_temps _ | Sil.Abstract _ | Sil.Nullify _ | Sil.Prune _
-> astate
end
module CFG = ProcCfg.OneInstrPerNode (ProcCfg.Normal)
module Analyzer = AbstractInterpreter.Make (CFG) (TransferFunctions)
let checker {Callbacks.tenv; summary; proc_desc} : Specs.summary =
let cfg = CFG.from_pdesc proc_desc in
(* start with empty set of uninit local vars and empty set of init formal params *)
let init =
{ RecordDomain.uninit_vars= UninitVars.empty
; RecordDomain.aliased_vars= AliasedVars.empty
; RecordDomain.prepost= (D.empty, D.empty) }
in
let invariant_map =
Analyzer.exec_cfg cfg (ProcData.make_default proc_desc tenv) ~initial:init ~debug:true
in
let report_uninit_value uninit_vars instr =
let report message loc =
let issue_id = IssueType.uninitialized_value.unique_id in
let ltr = [Errlog.make_trace_element 0 loc "" []] in
let exn = Exceptions.Checkers (issue_id, Localise.verbatim_desc message) in
Reporting.log_error summary ~loc ~ltr exn
in
match instr with
| Sil.Load (_, Exp.Lvar pv, _, loc)
| Sil.Store (_, _, Exp.Lvar pv, loc)
when exp_in_set (Exp.Lvar pv) uninit_vars
-> let message =
F.asprintf "The value read from %a was never initialized" Exp.pp (Exp.Lvar pv)
in
report message loc
| Sil.Call (_, Exp.Const Const.Cfun callee_pname, actual_params, loc, _)
when not intraprocedural_only -> (
match Summary.read_summary proc_desc callee_pname with
| Some {pre= deref_formal_params; post= _}
-> let deref_actual_params =
deref_actual_params callee_pname actual_params deref_formal_params
in
List.iter
~f:(fun (e, _) ->
if exp_in_set e uninit_vars && List.mem ~equal:Exp.equal deref_actual_params e then
let message =
F.asprintf "The value of %a is read in %a but was never initialized" Exp.pp e
Typ.Procname.pp callee_pname
in
report message loc)
actual_params
| _
-> () )
| _
-> ()
in
let report_on_node node =
List.iter (CFG.instr_ids node) ~f:(fun (instr, node_id_opt) ->
match node_id_opt with
| Some node_id -> (
match Analyzer.extract_pre node_id invariant_map with
| Some
{ RecordDomain.uninit_vars= uninitialized_vars
; RecordDomain.aliased_vars= _
; RecordDomain.prepost= _ }
-> report_uninit_value uninitialized_vars instr
| None
-> () )
| None
-> () )
in
List.iter (CFG.nodes cfg) ~f:report_on_node ;
match Analyzer.extract_post (CFG.id (CFG.exit_node cfg)) invariant_map with
| Some
{RecordDomain.uninit_vars= _; RecordDomain.aliased_vars= _; RecordDomain.prepost= pre, post}
-> Summary.update_summary {pre; post} summary
| None
-> L.(die InternalError)
"Analyzer failed to compute post for %a" Typ.Procname.pp (Procdesc.get_proc_name proc_desc)

@ -0,0 +1,66 @@
(*
* Copyright (c) 2017 - 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 F = Format
module L = Logging
(** Forward analysis to compute uninitialized variables at each program point *)
module Domain = AbstractDomain.InvertedSet (Var)
type summary = {pre: Domain.t; post: Domain.t}
module VarPair = struct
type t = Var.t * Var.t [@@deriving compare]
let pp fmt pair = F.fprintf fmt " (%a, %a)" Var.pp (fst pair) Var.pp (snd pair)
end
let pp_summary fmt {pre; post} =
F.fprintf fmt "@\n Pre: %a @\nPost: %a @\n" Domain.pp pre Domain.pp post
module Record
(Domain1 : AbstractDomain.S)
(Domain2 : AbstractDomain.S)
(Domain3 : AbstractDomain.S) =
struct
type astate =
{ uninit_vars: Domain1.astate
; aliased_vars: Domain2.astate
; prepost: Domain3.astate * Domain3.astate }
let ( <= ) ~lhs:({uninit_vars= lhs_uv; aliased_vars= lhs_av; prepost= lhs_pp} as lhs)
~rhs:({uninit_vars= rhs_uv; aliased_vars= rhs_av; prepost= rhs_pp} as rhs) =
if phys_equal lhs rhs then true
else Domain1.( <= ) ~lhs:lhs_uv ~rhs:rhs_uv && Domain2.( <= ) ~lhs:lhs_av ~rhs:rhs_av
&& Domain3.( <= ) ~lhs:(fst lhs_pp) ~rhs:(fst rhs_pp)
&& Domain3.( <= ) ~lhs:(snd lhs_pp) ~rhs:(snd rhs_pp)
let join ({uninit_vars= uv1; aliased_vars= av1; prepost= pp1} as astate1)
({uninit_vars= uv2; aliased_vars= av2; prepost= pp2} as astate2) =
if phys_equal astate1 astate2 then astate1
else
{ uninit_vars= Domain1.join uv1 uv2
; aliased_vars= Domain2.join av1 av2
; prepost= (Domain3.join (fst pp1) (fst pp2), Domain3.join (snd pp1) (snd pp2)) }
let widen ~prev:({uninit_vars= prev_uv; aliased_vars= prev_av; prepost= prev_pp} as prev)
~next:({uninit_vars= next_uv; aliased_vars= next_av; prepost= next_pp} as next) ~num_iters =
if phys_equal prev next then prev
else
{ uninit_vars= Domain1.widen ~prev:prev_uv ~next:next_uv ~num_iters
; aliased_vars= Domain2.widen ~prev:prev_av ~next:next_av ~num_iters
; prepost=
( Domain3.widen ~prev:(fst prev_pp) ~next:(fst next_pp) ~num_iters
, Domain3.widen ~prev:(snd prev_pp) ~next:(snd next_pp) ~num_iters ) }
let pp fmt {uninit_vars= uv; aliased_vars= av; prepost= pp} =
F.fprintf fmt "@\n uninit_vars: %a @\n aliased_vars: %a @\n prepost: (%a, %a)" Domain1.pp uv
Domain2.pp av Domain3.pp (fst pp) Domain3.pp (snd pp)
end

@ -0,0 +1,20 @@
# Copyright (c) 2017 - 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.
TESTS_DIR = ../../..
ANALYZER = checkers
# see explanations in cpp/errors/Makefile for the custom isystem
CLANG_OPTIONS = -x c++ -std=c++11 -nostdinc++ -isystem$(MODELS_DIR)/cpp/include -isystem$(CLANG_INCLUDES)/c++/v1/ -c
INFER_OPTIONS = --uninit --ml-buckets cpp --no-filtering --debug-exceptions --project-root $(TESTS_DIR)
INFERPRINT_OPTIONS = --issues-tests
SOURCES = uninit.mm
include $(TESTS_DIR)/clang.make
infer-out/report.json: $(MAKEFILE_LIST)

@ -0,0 +1,199 @@
/*
* Copyright (c) 2017 - 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.
*/
void init(int* i) { *i = 10; }
void init_bool(bool* i) { *i = false; }
void no_init(int* i) { i = 0; }
void no_init_bool(bool* i) { i = 0; }
int inc(int x) { return x++; }
// error is detected before call as we copy x
// so no need to put it in the summary
int no_init_return_bad() {
int x;
return x; // error
}
void bad1() {
int a;
int b = a; // Error
int c = b; // Error but we do not report as it depends from line 20
}
int bad2() {
int a;
int b = 0;
int c = 0;
no_init(&a);
b = a; // error
}
int bad3() {
int a;
int b = 0;
int c = 0;
no_init(&a);
c = inc(a); // error
}
int ok1() {
int a;
int b = 0;
int c = 0;
init(&a);
c = a; // OK
}
int ok2() {
int a;
int b = 0;
int c = 0;
init(&a);
c = inc(a); // ok
}
int bad4() {
int a;
int b = 0;
int c = 0;
no_init(&a);
b = a; // report here error
c = inc(b); // do not report as it depends from line 31
return 0;
}
// this function shows that we correctly reportat
// line 88 but not report the error at line 90
int bad5() {
int a;
int b = 0;
int c = 0;
no_init(&a);
b = a; // error
return b; // should not report as it depends from line 31
}
// this shows that in case a function return an uninit value, it gets the blame
// rather than the caller.
int blame_on_callee() {
int a;
int c = no_init_return_bad();
a = c; // we don't flag the error here as it is flagged in no_init_return
// definition
return 0;
}
void maybe_init(int y, int* formal) {
if (y == 0) {
*formal = 5;
};
}
void must_init(int y, int* formal) {
if (y == 0) {
*formal = 5;
} else {
*formal = 17;
};
}
int call_maybe_init_bad(int y) {
int x;
maybe_init(y, &x);
return x;
}
int call_must_init_ok(int y) {
int x;
must_init(y, &x);
return x;
}
void square_init(int x, int& res) { res = x * x; }
int square_no_init(int x, int& res) { return res * res; }
void use_square_OK() {
int i;
square_init(2, i);
}
void use_square_bad() {
int i;
i = square_no_init(2, i); // Error
}
int no_deref(int* x) {
int* y = 0;
x = y;
return *x; // this is not actually a deref of a formal
}
void init_x(int* x) {
int* y;
y = x;
*y = 25; // this is writing x
}
int use_init_x_OK() {
int a;
init_x(&a);
return a;
}
void bool1_bad() {
bool a;
bool b = a;
bool c = b;
}
int bool2_bad() {
bool a;
bool b = 0;
bool c = 0;
no_init_bool(&a);
b = a; // error
}
int bool1_ok() {
bool a;
bool b = 0;
bool c = 0;
init_bool(&a);
c = a; // OK
}

@ -0,0 +1,4 @@
codetoanalyze/cpp/uninit/uninit.mm, bad1, 2, UNINITIALIZED_VALUE, []
codetoanalyze/cpp/uninit/uninit.mm, branch1_FP, 11, UNINITIALIZED_VALUE, []
codetoanalyze/cpp/uninit/uninit.mm, loop1_FP, 10, UNINITIALIZED_VALUE, []
codetoanalyze/cpp/uninit/uninit.mm, no_init_return_bad, 2, UNINITIALIZED_VALUE, []

@ -0,0 +1,139 @@
/*
* Copyright (c) 2017 - 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.
*/
void init(int* i) { *i = 10; }
void init_bool(bool* i) { *i = false; }
void no_init(int* i) {}
void no_init_bool(bool* i) {}
int inc(int x) { return x + 1; }
// error is detected before call as we copy x
// so no need to put it in the summary
int no_init_return_bad() {
int x;
return x; // error
}
int bad1() {
int a;
int b = a; // Error
int c = b; // Error but we do not report as it depends from line 20
return c;
}
int ok1() {
int a;
int b;
no_init(&a);
b = a; // OK (only intraprocedural)
return b;
}
int ok2() {
int a;
int c;
no_init(&a);
c = inc(a); // OK (only intraprocedural)
return c;
}
int ok3() {
int a;
int c;
init(&a);
c = a; // OK
return c;
}
int ok4() {
int a;
int c;
init(&a);
c = inc(a); // ok
return c;
}
int ok5() {
int a;
int b;
int c;
no_init(&a);
b = a; // ok (only intraprocedural)
c = inc(b); // do not report as it depends from line 31
return c;
}
void square_init(int x, int& res) { res = x * x; }
int square_no_init(int x, int& res) { return res * res; }
void use_square_ok1() {
int i;
square_init(2, i);
}
int use_square_ok2() {
int i;
i = square_no_init(2, i); // OK only intraprocedural
return i;
}
bool getOK(void);
int branch1_FP() {
int size;
bool ok = getOK();
if (ok) {
size = 1;
}
if (ok) {
return size;
}
return 0;
}
int loop1_FP() {
int size;
for (;;) {
size = 1;
if (getOK())
break;
}
return size;
}
int ok6() {
int x;
x = 7;
return x;
}
Loading…
Cancel
Save